Merge remote-tracking branch 'goog/upstream-master' Test: none Change-Id: If9fbd37d37d42f621a84a3a71379a72a127c0d4d
diff --git a/.git.pre-commit b/.git.pre-commit new file mode 100755 index 0000000..6f5361f --- /dev/null +++ b/.git.pre-commit
@@ -0,0 +1,30 @@ +#!/bin/sh + +# This file will be used as .git/hooks/pre-commit . +# However, it should be edited as .git.pre-commit . +# You can install it by running: ant prep + +# Fail if any command fails +set -e + +# "ant -e check-style" would check every file; on commit we only need to +# check files that changed. +# Need to keep checked files in sync with formatted.java.files in +# build.xml. Otherwise `ant reformat` might not reformat a file that this +# hook complains about. +CHANGED_JAVA_FILES=`git diff --staged --name-only --diff-filter=ACM | grep '\.java$' | grep -v '/jdk/' | grep -v 'stubparser/' | grep -v '/eclipse/' ` || true +# echo CHANGED_JAVA_FILES "'"${CHANGED_JAVA_FILES}"'" +if [ ! -z "$CHANGED_JAVA_FILES" ]; then + (((cd .run-google-java-format && git pull -q) || git clone -q https://github.com/plume-lib/run-google-java-format.git .run-google-java-format) || true) + ## For debugging: + # echo "CHANGED_JAVA_FILES: ${CHANGED_JAVA_FILES}" + python .run-google-java-format/check-google-java-format.py --aosp ${CHANGED_JAVA_FILES} +fi + +# This is to handle non-.java files, since the above already handled .java files. +# May need to remove files that are allowed to have trailing whitespace. +CHANGED_STYLE_FILES=`git diff --staged --name-only --diff-filter=ACM | grep -v '.png$' | grep -v '.xcf$'` || true +if [ ! -z "$CHANGED_STYLE_FILES" ]; then + # echo "CHANGED_STYLE_FILES: ${CHANGED_STYLE_FILES}" + grep -q '[[:blank:]]$' ${CHANGED_STYLE_FILES} 2>&1 && (echo "Some files have trailing whitespace:" && grep -l '[[:blank:]]$' ${CHANGED_STYLE_FILES} && exit 1) +fi
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aeb94e7 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,71 @@ +bin +# *.class +*_Generated.class +*_temp.class +*~ +TAGS + +annotation-file-utilities/annotation-file-format.aux +annotation-file-utilities/annotation-file-format.blg +annotation-file-utilities/annotation-file-format.dvi +annotation-file-utilities/annotation-file-format.haux +annotation-file-utilities/annotation-file-format.html +annotation-file-utilities/annotation-file-format.htoc +annotation-file-utilities/annotation-file-format.log +annotation-file-utilities/annotation-file-format.out +annotation-file-utilities/annotation-file-format.pdf +annotation-file-utilities/annotation-file-format.ps +annotation-file-utilities/annotation-file-format.toc +#annotation-file-utilities/annotation-file-utilities.jar +annotation-file-utilities/annotation-tools.zip +annotation-file-utilities/design.aux +annotation-file-utilities/design.log +annotation-file-utilities/design.pdf + +annotation-file-utilities/javadoc + +annotation-file-utilities/tests/abbreviated/*.diff +annotation-file-utilities/tests/abbreviated/*.log +annotation-file-utilities/tests/abbreviated/*.output +annotation-file-utilities/tests/ad-hoc/bridge/A.class +annotation-file-utilities/tests/ad-hoc/bridge/C.class +annotation-file-utilities/tests/ad-hoc/bridge/C.jaif +annotation-file-utilities/tests/source-extension/*.diff +annotation-file-utilities/tests/source-extension/*.log +annotation-file-utilities/tests/source-extension/*.output +annotation-file-utilities/tests/Makefile.user +annotation-file-utilities/tests/system-test/out1 +annotation-file-utilities/tests/system-test/out3 +annotation-file-utilities/tests/system-test/out4.class +annotation-file-utilities/tests/system-test/out2.diff +annotation-file-utilities/tests/system-test/out2.jaif +annotation-file-utilities/tests/system-test/out5.diff +annotation-file-utilities/tests/system-test/out5.jaif +annotation-file-utilities/tests/*.diff +annotation-file-utilities/tests/*.log +annotation-file-utilities/tests/*.output + +asmx/output + +scene-lib/javadoc +scene-lib/reports +scene-lib/test/annotations/tests/executable/example-output.jaif +scene-lib/test/annotations/tests/executable/example-stdout.jaif + +*.class-via-classfile-scene.txt +*.class-correct-scene.txt +*.class-from-indexfile.txt +*.class-generated-scene.txt + +.metadata + +annotation-file-utilities/figures/*.eps +annotation-file-utilities/figures/*.pdf +annotation-file-utilities/figures/*.png +annotation-file-utilities/*.png + +.run-google-java-format + +# User customizations +Makefile.user +build.properties
diff --git a/.travis-build-without-test.sh b/.travis-build-without-test.sh new file mode 100755 index 0000000..26f89c6 --- /dev/null +++ b/.travis-build-without-test.sh
@@ -0,0 +1,34 @@ +#!/bin/bash +ROOT=$TRAVIS_BUILD_DIR/.. + +# Fail the whole script if any command fails +set -e + +export SHELLOPTS + +SLUGOWNER=${TRAVIS_REPO_SLUG%/*} +if [[ "$SLUGOWNER" == "" ]]; then + SLUGOWNER=typetools +fi + + +# jsr308-langtools +if [ -d ../jsr308-langtools ] ; then + (cd ../jsr308-langtools && hg pull && hg update) +else + set +e + echo "Running: hg identify https://bitbucket.org/${SLUGOWNER}/jsr308-langtools &>-" + hg identify https://bitbucket.org/${SLUGOWNER}/jsr308-langtools &>- + if [ "$?" -ne 0 ]; then + SLUGOWNER=typetools + fi + set -e + echo "Running: (cd .. && hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools)" + (cd .. && (hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools || hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools)) + echo "... done: (cd .. && hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools)" +fi +(cd ../jsr308-langtools/ && ./.travis-build-without-test.sh) + +## Compile +echo "running \"ant compile\" for annotation-tools" +ant compile
diff --git a/.travis-build.sh b/.travis-build.sh new file mode 100755 index 0000000..d1d5c91 --- /dev/null +++ b/.travis-build.sh
@@ -0,0 +1,18 @@ +#!/bin/bash + +# Fail the whole script if any command fails +set -e + +export SHELLOPTS + +./.travis-build-without-test.sh + +ant all + +ant check-style + +# TODO: when codebase is reformatted +# ant check-format + +# TODO: When we require Java 8 for compilation: +# ant html-validate
diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c7c09e5 --- /dev/null +++ b/.travis.yml
@@ -0,0 +1,24 @@ +language: java + +script: ./.travis-build.sh + +sudo: false + +jdk: + - oraclejdk8 + +jobs: + include: + - stage: trigger-downstream + jdk: oraclejdk8 + script: | + echo "TRAVIS_BRANCH=$TRAVIS_BRANCH TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST" + if [[ ($TRAVIS_BRANCH == master) && + ($TRAVIS_PULL_REQUEST == false) ]] ; then + curl -LO https://raw.github.com/mernst/plume-lib/master/bin/trigger-travis.sh + SLUGOWNER=${TRAVIS_REPO_SLUG%/*} + sh trigger-travis.sh ${SLUGOWNER} checker-framework $TRAVISTOKEN + fi + +git: + depth: 9
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..89de354 --- /dev/null +++ b/LICENSE
@@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..89de354 --- /dev/null +++ b/LICENSE.txt
@@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.
diff --git a/METADATA b/METADATA new file mode 100644 index 0000000..8ba6c6e --- /dev/null +++ b/METADATA
@@ -0,0 +1,16 @@ +name: "annotation_tools" +description: + "Annotation File Utilities, used for inserting external annotations to source files" + +third_party { + url { + type: HOMEPAGE + value: "https://github.com/typetools/annotation-tools" + } + url { + type: ARCHIVE + value: "https://checkerframework.org/annotation-file-utilities/annotation-tools-3.6.45.zip" + } + version: "3.6.45" + last_upgrade_date { year: 2017 month: 9 day: 28 } +}
diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000..cfdec88 --- /dev/null +++ b/OWNERS
@@ -0,0 +1,3 @@ +pszczepaniak@google.com +narayan@google.com +
diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..9095538 --- /dev/null +++ b/README.txt
@@ -0,0 +1,38 @@ +Annotation Tools README file +---------------------------- + +This directory contains the Annotation Tools. +When distributed, this is known as the Annotation File Utilities, which is +one of its components; see the annotation-file-utilities subdirectory. + +The Annotation File Utilities homepage is: + https://checkerframework.org/annotation-file-utilities/ +and it also appears in this directory as: + annotation-file-utilities/annotation-file-utilities.html + + +asmx +---- + +asmx contains modifications to asm to allow it to read and write JSR +308 annotations to/from class files. + +The most modified classes are (in org.objectweb.asm): + * ClassReader + * ClassWriter + * ExtendedAnnotationVisitor + * ExtendedAnnotationWriter + +Most of the changes are delimited by +//jaime +// end jaime + +asmx was branched off of asm 2.2.2, available at either of these locations: + http://forge.objectweb.org/project/download.php?group_id=23&file_id=5769 + http://download.forge.objectweb.org/asm/asm-2.2.2.tar.gz +To see the changes, diff the current source files against the 2.2.2 +files. + +The diffs are complicated by the fact that in a few cases, a post-2.2.2 +version of a file was added to asmx. +One example is file src/org/objectweb/asm/optimizer/shrink.properties.
diff --git a/README.version b/README.version new file mode 100644 index 0000000..49ead1b --- /dev/null +++ b/README.version
@@ -0,0 +1,11 @@ +URL: https://github.com/typetools/annotation-tools +Version: 3.6.45 +License: MIT +Description: Annotation File Utilities, used for inserting external annotations to source files + +Local Modifications: + Check-in annotation-file-utilities/annotation-file-utilities.jar from + https://checkerframework.org/annotation-file-utilities/annotation-tools-3.6.45.zip + Remove annotation-file-utilities/annotation-file-utilities.jar from .gitignore yet. + +
diff --git a/annotation-file-utilities/.classpath b/annotation-file-utilities/.classpath new file mode 100644 index 0000000..a5bc5ce --- /dev/null +++ b/annotation-file-utilities/.classpath
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry combineaccessrules="false" kind="src" path="/jsr308-langtools"/> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/asmx"/> + <classpathentry combineaccessrules="false" kind="src" path="/scene-lib"/> + <classpathentry kind="lib" path="lib/guava-20.0.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> + <classpathentry combineaccessrules="false" kind="src" path="/plume-lib"/> + <classpathentry kind="output" path="bin"/> +</classpath>
diff --git a/annotation-file-utilities/.project b/annotation-file-utilities/.project new file mode 100644 index 0000000..e028bcb --- /dev/null +++ b/annotation-file-utilities/.project
@@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>annotation-file-utilities</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription>
diff --git a/annotation-file-utilities/README.txt b/annotation-file-utilities/README.txt new file mode 100644 index 0000000..30a9741 --- /dev/null +++ b/annotation-file-utilities/README.txt
@@ -0,0 +1,66 @@ +Annotation File Utilities README file + +For user documentation, see file annotation-file-utilities.html . + +=========================================================================== + +Contents + +The contents of this directory are: + +annotation-file-utilities.html + Annotation File Utilities documentation. + Most users should only have to read this file. +annotation-file-format.{html,pdf} + Describes the annotation file format. +scripts/ + Contains Unix and Windows programs for transferring annotations among + Java, class, and annotation files. +annotation-file-utilities.jar + Java library used by the programs. +build.xml, src/, lib/, tests/ + For developers only: buildfile, source code, libraries, tests. + +=========================================================================== + +Notes + +To build annotation-file-format.{html,pdf} your BIBINPUTS environment +variable must be set like so: + +export BIBINPUTS=.:/path/to/plume/bib + +plume-bib is available at https://github.com/mernst/plume-bib . + +=========================================================================== + +Making a release + +To make a release (a distribution): + +Be sure to run these instructions in an account that uses JDK 7, not JDK 8. + +Write a description of the most significant changes in: + changelog.html +It may be helpful to examine the changes since the last release: + git log v3.5.3.. + git diff v3.5.3.. + +Manually update the version number and date in the following places: + annotation-file-utilities.html (in "Installation" section) + changelog.html + annotations/scene-lib/src/annotations/io/classfile/ClassFileReader.java + +Create and post to the web the entire distribution: + ant -e web + +Run + checklink -q -r https://checkerframework.org/annotation-file-utilities/ +and if there are any problems, re-make the distribution. + +Tag the release, for example: + git tag v3.5.3 + +Send email to: checker-framework-discuss@googlegroups.com + +===========================================================================
diff --git a/annotation-file-utilities/annotation-file-format.bbl b/annotation-file-utilities/annotation-file-format.bbl new file mode 100644 index 0000000..6589adc --- /dev/null +++ b/annotation-file-utilities/annotation-file-format.bbl
@@ -0,0 +1,26 @@ +\newcommand{\etalchar}[1]{$^{#1}$} +\begin{thebibliography}{DDE{\etalchar{+}}11} + +\bibitem[Che]{CF} +{Checker Framework} website. +\newblock \url{https://checkerframework.org/}. + +\bibitem[DDE{\etalchar{+}}11]{DietlDEMS2011} +Werner Dietl, Stephanie Dietzel, Michael~D. Ernst, K{\i}van{\c{c}} Mu{\c{s}}lu, + and Todd Schiller. +\newblock Building and using pluggable type-checkers. +\newblock In {\em ICSE 2011, Proceedings of the 33rd International Conference + on Software Engineering}, pages 681--690, Waikiki, Hawaii, USA, May 2011. + +\bibitem[Ern13]{JSR308-webpage-201310} +Michael~D. Ernst. +\newblock {Type Annotations} specification ({JSR} 308). +\newblock \url{https://checkerframework.org/jsr308/}, October 2013. + +\bibitem[LBR06]{LeavensBR2006:JML} +Gary~T. Leavens, Albert~L. Baker, and Clyde Ruby. +\newblock Preliminary design of {JML}: A behavioral interface specification + language for {Java}. +\newblock {\em ACM SIGSOFT Software Engineering Notes}, 31(3), March 2006. + +\end{thebibliography}
diff --git a/annotation-file-utilities/annotation-file-format.bib b/annotation-file-utilities/annotation-file-format.bib new file mode 100644 index 0000000..cfcba2f --- /dev/null +++ b/annotation-file-utilities/annotation-file-format.bib
@@ -0,0 +1,11 @@ +@Misc{ASM, + key = "ASM website", + title = "{ASM} website", + howpublished = "\url{http://asm.ow2.org/}", +} + +@Misc{CF, + key = "Checker Framework website", + title = "{Checker Framework} website", + howpublished = "\url{https://checkerframework.org/}", +}
diff --git a/annotation-file-utilities/annotation-file-format.tex b/annotation-file-utilities/annotation-file-format.tex new file mode 100644 index 0000000..5825722 --- /dev/null +++ b/annotation-file-utilities/annotation-file-format.tex
@@ -0,0 +1,1467 @@ +\documentclass{article} +\usepackage{fancyvrb} +\usepackage{graphicx} +\usepackage{fullpage} +\usepackage{relsize} +\usepackage{url} +\usepackage{hevea} +\usepackage[shortcuts]{extdash} +\usepackage{textcomp} +% \usepackage{verbdef} + +\def\topfraction{.9} +\def\dbltopfraction{\topfraction} +\def\floatpagefraction{\topfraction} % default .5 +\def\dblfloatpagefraction{\topfraction} % default .5 +\def\textfraction{.1} + +%HEVEA \footerfalse % Disable hevea advertisement in footer + +\newcommand{\code}[1]{\ifmmode{\mbox{\relax\ttfamily{#1}}}\else{\relax\ttfamily #1}\fi} +%% Hevea version omits "\smaller" +%HEVEA \renewcommand{\code}[1]{\ifmmode{\mbox{\ttfamily{#1}}}\else{\ttfamily #1}\fi} + +\newcommand{\includeimage}[2]{ +\begin{center} +\ifhevea\imgsrc{#1.png}\else +\resizebox{!}{#2}{\includegraphics{figures/#1}} +\vspace{-1.5\baselineskip} +\fi +\end{center}} + +% Add line between figure and text +\makeatletter +\def\topfigrule{\kern3\p@ \hrule \kern -3.4\p@} % the \hrule is .4pt high +\def\botfigrule{\kern-3\p@ \hrule \kern 2.6\p@} % the \hrule is .4pt high +\def\dblfigrule{\kern3\p@ \hrule \kern -3.4\p@} % the \hrule is .4pt high +\makeatother + + +\title{Annotation File Format Specification} +% Hevea ignores \date, so move the date into \author +\author{\url{https://checkerframework.org/annotation-file-utilities/} \\ +\today} +\date{} + +\begin{document} + +\maketitle + +%HEVEA \setcounter{tocdepth}{2} +\tableofcontents + +\section{Purpose: External storage of annotations\label{purpose}} + +Java annotations are meta-data about Java program elements, as in +``\code{@Deprecated class Date \{ \ldots\ \}}''. +Ordinarily, Java annotations are written in the source code of a +\code{.java} Java source file. When \code{javac} compiles the source code, +it inserts the annotations in the resulting \code{.class} file (as +``attributes''). + +Sometimes, it is convenient to specify the annotations outside the source +code or the \code{.class} file. +\begin{itemize} +%BEGIN LATEX +\itemsep 0pt \parskip 0pt +%END LATEX +\item + When source code is not available, a textual file provides a format for + writing and storing annotations that is much easier to read and modify + than a \code{.class} file. Even if the eventual purpose is to insert the + annotations in the \code{.class} file, the annotations must be specified + in some textual format first. +\item + Even when source code is available, sometimes it should not be changed, + yet annotations must be stored somewhere for use by tools. +\item + A textual file for annotations can eliminate code clutter. A developer + performing some specialized task (such as code verification, + parallelization, etc.)\ can store annotations in an annotation file without + changing the main version of the source code. (The developer's private + version of the code could contain the annotations, but the developer + could move them to the separate file before committing changes.) +\item + Tool writers may find it more convenient to use a textual file, rather + than writing a Java or \code{.class} file parser. +\item + When debugging annotation-processing tools, a textual file format + (extracted from the Java or \code{.class} files) is easier to read and + is easier for use in testing. +\end{itemize} + +All of these uses require an external, textual file format for Java annotations. +The external file format should be easy for people to create, read, and +modify. +% +An ``annotation file'' serves this purpose by specifying a set of +Java annotations. +The Annotation File Utilities +(\url{https://checkerframework.org/annotation-file-utilities/}) are a set +of tools that process annotation files. + +The file format discussed in this document supports both standard Java SE 5 +declaration annotations and also the type annotations that are introduced by Java SE 8. +The file format provides a simple syntax to represent the structure of a Java +program. For annotations in method bodies of \code{.class} files the annotation +file closely follows +section ``Class File Format Extensions'' of the JSR 308 design document~\cite{JSR308-webpage-201310}, +which explains how the annotations are stored in the \code{.class} +file. +In that sense, the current design is extremely low-level, and users +probably would not want to write the files by hand (but they might fill in a +template that a tool generated automatically). As future work, we should +design a more +user-friendly format that permits Java signatures to be directly specified. +For \code{.java} source files, the file format provides a separate, higher-level +syntax for annotations in method bodies. + + + +%% I don't like this, as it may force distributing logically connected +%% elements all over a file system. Users should be permitted, but not +%% forced, to adopt such a file structure. -MDE +% Each file corresponds to exactly one +% ``.class'' file, so (for instance) inner classes are written in +% separate annotation files, named in the same ``{\tt +% OuterClass\$InnerClass}'' pattern as the ``.class'' file. + + +By convention, an annotation file ends with ``\code{.jaif}'' (for ``Java +annotation index file''), but this is not required. + + +% \verbdef\lineend|"\n"| + +%BEGIN LATEX +\DefineShortVerb{\|} +\SaveVerb{newline}|\n| +\UndefineShortVerb{\|} +\newcommand{\lineend}{\bnflit{\UseVerb{newline}}} +%END LATEX +%HEVEA \newcommand{\bs}{\char"5C} +%HEVEA \newcommand{\lineend}{\bnflit{\bs{}n}} + +% literal +\newcommand{\bnflit}[1]{\textrm{``}\textbf{#1}\textrm{''}} +% non-terminal +\newcommand{\bnfnt}[1]{\textsf{\emph{#1}}} +% comment +\newcommand{\bnfcmt}{\rm \# } +% alternative +\newcommand{\bnfor}{\ensuremath{|}} + + +\section{Grammar\label{grammar}} + +This section describes the annotation file format in detail by presenting it in +the form of a grammar. Section~\ref{grammar-conventions} details the conventions +of the grammar. Section~\ref{java-file-grammar} shows how to represent the +basic structure of a Java program (classes, methods, etc.) in an annotation +file. Section~\ref{annotations-grammar} shows how to add annotations to an +annotation file. + +\subsection{Grammar conventions\label{grammar-conventions}} + +Throughout this document, ``name'' is any valid Java simple name or +binary name, ``type'' is any valid type, and ``value'' is any +valid Java constant, and quoted strings are literal values. +% +The Kleene qualifiers ``*'' (zero or more), ``?'' (zero or one), and ``+'' +(one or more) denote plurality of a grammar element. +% +A vertical bar (``\bnfor'') separates alternatives. +Parentheses (``()'') denote grouping, and square brackets (``[]'') +denote optional syntax, which is equivalent to ``( \ldots\ )\ ?''\ but more concise. +We use the hash/pound/octothorpe symbol (``\#'') for comments within the grammar. + +In the annotation file, +besides its use as token separator, +whitespace (excluding +newlines) is optional with one exception: no space is permitted +between an ``@'' character and a subsequent name. Indentation is +ignored, but is encouraged to maintain readability of the hierarchy of +program elements in the class (see the example in Section~\ref{example}). + +Comments can be written throughout the annotation file using the double-slash +syntax employed by Java for single-line comments: anything following +two adjacent slashes (``//'') until the first newline is a comment. +This is omitted from the grammar for simplicity. +Block comments (``/* \ldots\ */'') are not allowed. + +The line end symbol \lineend{} is used for all the different line end +conventions, that is, Windows- and Unix-style newlines are supported. + + +\subsection{Java file grammar\label{java-file-grammar}} + +This section shows how to represent the basic structure of a Java program +(classes, methods, etc.) in an annotation file. For Java elements that can +contain annotations, this section will reference grammar productions contained +in Section~\ref{annotations-grammar}, which describes how annotations are used +in an annotation file. + +An annotation file has the same basic structure as a Java program. That is, +there are packages, classes, fields and methods. + +The annotation file may omit certain program elements --- for instance, it +may mention only some of the packages in a program, or only some of the +classes in a package, or only some of the fields or methods of a class. +Program elements that do not appear in the annotation file are treated as +unannotated. + + +\subsubsection{Package definitions\label{package-definitions}} + +At the root of an annotation file is one or more package definitions. +A package definition describes a package containing a list of annotation +definitions and classes. A package definition also contains any +annotations on the package (such as those from a +\code{package-info.java} file). + +\begin{tabbing} +\qquad \= \kill +\bnfnt{annotation-file} ::= \\ +\qquad \bnfnt{package-definition}+ \\ +\\ +\bnfnt{package-definition} ::= \\ +\qquad \bnflit{package} ( \bnflit{:} ) \bnfor{} ( \bnfnt{name} \bnflit{:} \bnfnt{decl-annotation}* ) \lineend \\ +\qquad ( \bnfnt{annotation-definition} \bnfor{} \bnfnt{class-definition} ) * +\end{tabbing} + +\noindent +Use a package line of \code{package:} for the default package. Note that +annotations on the default package are not allowed. + + +\subsubsection{Class definitions\label{class-definitions}} + +A class definition describes the annotations present on a class declaration, +as well fields and methods of the class. It is organized according to +the hierarchy of fields and methods in the class. +Note that we use \bnfnt{class-definition} also for interfaces, enums, and +annotation types (to specify annotations in an existing annotation type, not to +be confused with \bnfnt{annotation-definition}s described in +Section~\ref{annotation-definitions}, which defines annotations to be used +throughout an annotation file); for syntactic simplicity, we use \bnflit{class} +for +all such definitions. +% TODO: add test cases for this. + +Inner classes are treated as ordinary classes whose names happen to contain +\code{\$} signs and must be defined at the top level of a class definition file. +(To change this, the grammar would have to be extended with a closing +delimiter for classes; otherwise, it would be ambiguous whether a +field or method appearing after an inner class definition belonged to the +inner class or the outer class.) The syntax for inner class names is the same as +is used by the \code{javac} compiler. A good way to get an idea of the inner +class names for a class is to compile the class and look at the filenames of the +\code{.class} files that are produced. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{class-definition} ::= \\ +\qquad \bnflit{class} \bnfnt{name} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\ +% TODO: is the order really important? eg. can fields and methods not +% be mixed? +\qquad \bnfnt{typeparam-definition}* \\ +\qquad \bnfnt{typeparam-bound}* \\ +\qquad \bnfnt{extends}* \\ +\qquad \bnfnt{implements}* \\ +\qquad \bnfnt{field-definition}* \\ +\qquad \bnfnt{staticinit}* \\ +\qquad \bnfnt{instanceinit}* \\ +\qquad \bnfnt{method-definition}* +\end{tabbing} + +\noindent +Annotations on the \bnflit{class} line are annotations on the class declaration, +not the class name. + + +\paragraph{Type parameter definitions} + +The \bnfnt{typeparam-definition} production defines annotations on the +declaration of a type parameter, such as on \code{K} and \code{T} in + +\begin{verbatim} +public class Class<K> { + public <T> void m() { + ... + } +} +\end{verbatim} + +or on the type parameters on the left-hand side of a member reference, +as on \code{String} in \code{List<String>::size}. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{typeparam-definition} ::= \\ +\qquad \bnfcmt The integer is the zero-based type parameter index. \\ +\qquad \bnflit{typeparam} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\paragraph{Type Parameter Bounds} + +The \bnfnt{typeparam-bound} production defines annotations on a bound of a +type variable declaration, such as on \code{Number} and \code{Date} in + +\begin{verbatim} +public class Class<K extends Number> { + public <T extends Date> void m() { + ... + } +} +\end{verbatim} + +\begin{tabbing} +\qquad \= \kill +\bnfnt{typeparam-bound} ::= \\ +% The bound should really be a sub-element of the typeparam! +\qquad \bnfcmt The integers are respectively the parameter and bound indexes of \\ +\qquad \bnfcmt the type parameter bound~\cite{JSR308-webpage-201310}. \\ +\qquad \bnflit{bound} \bnfnt{integer} \bnflit{\&} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\paragraph{Implements and extends} + +The \bnfnt{extends} and \bnfnt{implements} productions +define annotations on the names of classes a class \code{extends} or +\code{implements}. +(Note: For interface declarations, \bnfnt{implements} rather than +\bnfnt{extends} defines annotations on the names of extended +interfaces.) + +\begin{tabbing} +\qquad \= \kill +\bnfnt{extends} ::= \\ +\qquad \bnflit{extends} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* \\ +\\ +\bnfnt{implements} ::= \\ +\qquad \bnfcmt The integer is the zero-based index of the implemented interface. \\ +\qquad \bnflit{implements} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\paragraph{Static and instance initializers} + +The \bnfnt{staticinit} and \bnfnt{instanceinit} productions +define annotations on code within static or instance initializer blocks. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{staticinit} ::= \\ +\qquad \bnfcmt The integer is the zero-based index of the implemented interface. \\ +\qquad \bnflit{staticinit} \bnflit{*} \bnfnt{integer} \bnflit{:} \lineend \\ +\qquad \bnfnt{compound-type}* +\\ +\bnfnt{instanceinit} ::= \\ +\qquad \bnfcmt The integer is the zero-based index of the implemented interface. \\ +\qquad \bnflit{instanceinit} \bnflit{*} \bnfnt{integer} \bnflit{:} \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\subsubsection{Field definitions\label{field-definitons}} + +A field definition can have annotations on the declaration, the type of the +field, or --- if in source code --- the field's initialization. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{field-definition} ::= \\ +\qquad \bnflit{field} \bnfnt{name} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\ +\qquad \bnfnt{type-annotations}* \\ +\qquad \bnfnt{expression-annotations}* +\end{tabbing} + +\noindent +Annotations on the \bnflit{field} line are on the field declaration, not the +type of the field. + +The \bnfnt{expression-annotations} production specifies annotations on the +initialization expression of a field. If a field is initialized at declaration +then in bytecode the initialization is moved to the constructor when the class +is compiled. Therefore for bytecode, annotations on the initialization +expression go in the constructor (see Section~\ref{method-definitions}), rather +than the field definition. Source code annotations for the field initialization +expression are valid on the field definition. + + +\subsubsection{Method definitions\label{method-definitions}} + +A method definition can have annotations on the method declaration, in the +method header (return type, parameters, etc.), as well as the method body. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{method-definition} ::= \\ +\qquad \bnflit{method} \bnfnt{method-key} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\ +\qquad \bnfnt{typeparam-definition}* \\ +\qquad \bnfnt{typeparam-bound}* \\ +\qquad \bnfnt{return-type}? \\ +\qquad \bnfnt{receiver-definition}? \\ +\qquad \bnfnt{parameter-definition}* \\ +% TODO: method throws +\qquad \bnfnt{variable-definition}* \\ +\qquad \bnfnt{expression-annotations}* +\end{tabbing} + +\noindent +The annotations on the \bnflit{method} line are on the method declaration, not +on the return value. The \bnfnt{method-key} consists of the name followed by the +signature in JVML format. For example, the following method + +\begin{verbatim} +boolean foo(int[] i, String s) { + ... +} +\end{verbatim} + +\noindent +has the \bnfnt{method-key}: + +\begin{verbatim} +foo([ILjava/lang/String;)Z +\end{verbatim} + +Note that the +signature is the erased signature of the method and does not contain generic +type information, but does contain the return type. Using \code{javap -s} makes +it easy to find the signature. The method keys ``\code{<init>}'' and +``\code{<clinit>}'' are used to name instance (constructor) and class (static) +initialization methods. (The name of the constructor---that is, the final +element of the class name---can be used in place of ``\code{<init>}''.) +For both instance and class initializers, the ``return type'' part of the +signature should be \code{V} (for \code{void}). + +% TODO: exception types in catch clause +% TODO: .class literals +% TODO: type arguments in constructor and method calls + + +\paragraph{Return type} + +A return type defines the annotations on the return type of a method +declaration. It is also used for the result of a constructor. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{return-type} ::= \\ +\qquad \bnflit{return:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\paragraph{Receiver definition} + +A receiver definition defines the annotations on the type of the receiver +parameter in a method declaration. A method receiver is the implicit formal +parameter, \code{this}, used in non-static methods. For source code insertion, +the receiver parameter will be inserted if it does not already exist. + +Only inner classes have a receiver. A top-level constructor does not have +a receiver, though it does have a result. The type of a constructor result +is represented as a return type. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{receiver-definition} ::= \\ +\qquad \bnflit{receiver:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\paragraph{Parameter definition} + +A formal parameter definition defines the annotations on a method formal +parameter declaration and the type of a method formal parameter, but +\emph{not} the receiver formal parameter. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{parameter-definition} ::= \\ +\qquad \bnfcmt The integer is the zero-based index of the formal parameter in the method. \\ +\qquad \bnflit{parameter} \bnfnt{integer} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\ +\qquad \bnfnt{type-annotations}* +\end{tabbing} + +\noindent +The annotations on the \bnflit{parameter} line are on the formal parameter +declaration, not on the type of the parameter. A parameter index of 0 is the +first formal parameter. The receiver parameter is not index 0. Use the +\bnfnt{receiver-definition} production to annotate the receiver parameter. + + +\subsection{Bytecode Locations\label{bytecode-locations}} + +Certain elements in the body of a method or the initialization expression of a +field can be annotated. The \bnfnt{expression-annotations} rule describes the +annotations that can be added to a method body or a field initialization +expression: + +\begin{tabbing} +\qquad \= \kill +\bnfnt{expression-annotations} ::= \\ +\qquad \bnfnt{typecast}* \\ +\qquad \bnfnt{instanceof}* \\ +\qquad \bnfnt{new}* \\ +\qquad \bnfnt{call}* \\ +\qquad \bnfnt{reference}* \\ +\qquad \bnfnt{lambda}* \\ +\qquad \bnfnt{source-insert-typecast}* \\ +\qquad \bnfnt{source-insert-annotation}* +\end{tabbing} + +\noindent +Additionally, a variable declaration in a method body can be annotated with the +\bnfnt{variable-definition} rule, which appears below. + +Because of the differences between Java source code and \code{.class} files, +the syntax for specifying code locations is different for \code{.class} files +and source code. For \code{.class} files we use a syntax called ``bytecode +offsets''. For source code we use a different syntax called ``source code +indexes''. These are both described below. + +If you wish to be able to insert a given code annotation in both a \code{.class} file and a source +code file, the annotation file must redundantly specify the annotation's bytecode offset and source +code index. This can be done in a single \code{.jaif} file or two separate +\code{.jaif} files. It is not necessary to include +redundant information to insert annotations on signatures in both \code{.class} +files and source code. + +Additionally, a new typecast with annotations (rather than an annotation added to an +existing typecast) can be inserted into source code. This uses a third +syntax that is described below under ``AST paths''. +A second way to insert a typecast is by specifying just an annotation, not +a full typecast (\code{insert-annotation} instead of +\code{insert-typecast}). In this case, the source annotation insertion +tool generates a full typecast if Java syntax requires one. + + +\subsubsection{Bytecode offsets\label{bytecode-offsets}} + +For locations in bytecode, the +annotation file uses offsets into the bytecode array of the class file to +indicate the specific expression to which the annotation refers. Because +different compilation strategies yield different \code{.class} files, a +tool that maps such annotations from an annotation file into source code must +have access to the specific \code{.class} file that was used to generate +the annotation file. The +\code{javap -v} command is an effective technique to discover bytecode +offsets. Non-expression annotations such as those on methods, +fields, classes, etc., do not use a bytecode offset. + + +\subsubsection{Source code indexes\label{source-code-indexes}} + +For locations in source code, the annotation file indicates the kind of +expression, plus a zero-based index to indicate which occurrence of that kind of +expression. For example, + +\begin{verbatim} +public void method() { + Object o1 = new @A String(); + String s = (@B String) o1; + Object o2 = new @C Integer(0); + Integer i = (@D Integer) o2; +} +\end{verbatim} + +\noindent +\code{@A} is on new, index 0. \code{@B} is on typecast, index 0. \code{@C} is on +new, index 1. \code{@D} is on typecast, index 1. + +Source code indexes only include occurrences in the class that exactly matches +the name of the enclosing \bnfnt{class-definition} rule. Specifically, +occurrences in nested classes are not included. Use a new +\bnfnt{class-definition} rule with the name of the nested class for source code +insertions in a nested class. + + +\subsubsection{Code locations grammar\label{code-grammar}} + +For each kind of expression, the grammar contains a separate location rule. +This location rule contains the bytecode offset syntax followed by the +source code index syntax. + +The grammar uses \bnflit{\#} for bytecode offsets and \bnflit{*} for source code indexes. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{variable-location} ::= \\ +\qquad \bnfcmt Bytecode offset: the integers are respectively the index, start, and length \\ +\qquad \bnfcmt fields of the annotations on this variable~\cite{JSR308-webpage-201310}. \\ +\qquad (\bnfnt{integer} \bnflit{\#} \bnfnt{integer} \bnflit{+} \bnfnt{integer}) \\ +\qquad \bnfcmt Source code index: the \bnfnt{name} is the identifier of the local variable. \\ +\qquad \bnfcmt The \bnfnt{integer} is the optional zero-based index of the intended local \\ +\qquad \bnfcmt variable within all local variables with the given \bnfnt{name}. \\ +\qquad \bnfcmt The default value for the index is zero. \\ +\qquad \bnfor{} (\bnfnt{name} [\bnflit{*} \bnfnt{integer}]) \\ +\\ +\bnfnt{variable-definition} ::= \\ +\qquad \bnfcmt The annotations on the \bnflit{local} line are on the variable declaration, \\ +\qquad \bnfcmt not the type of the variable. \\ +\qquad \bnflit{local} \bnfnt{variable-location} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\ +\qquad \bnfnt{type-annotations}* \\ +\\ +\bnfnt{typecast-location} ::= \\ +\qquad \bnfcmt Bytecode offset: the first integer is the offset field and the optional \\ +\qquad \bnfcmt second integer is the type index of an intersection type~\cite{JSR308-webpage-201310}. \\ +\qquad \bnfcmt The type index defaults to zero if not specified. \\ +\qquad (\bnflit{\#} \bnfnt{integer} [ \bnflit{,} \bnfnt{integer} ]) \\ +\qquad \bnfcmt Source code index: the first integer is the zero-based index of the typecast \\ +\qquad \bnfcmt within the method and the optional second integer is the type index of an \\ +\qquad \bnfcmt intersection type~\cite{JSR308-webpage-201310}. The type index defaults to zero if not specified. \\ +\qquad \bnfor{} (\bnflit{*} \bnfnt{integer} [ \bnflit{,} \bnfnt{integer} ]) \\ +\\ +\bnfnt{typecast} ::= \\ +\qquad \bnflit{typecast} \bnfnt{typecast-location} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* \\ +\\ +\bnfnt{instanceof-location} ::= \\ +\qquad \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\ +\qquad (\bnflit{\#} \bnfnt{integer}) \\ +\qquad \bnfcmt Source code index: the integer is the zero-based index of the \code{instanceof} \\ +\qquad \bnfcmt within the method. \\ +\qquad \bnfor{} (\bnflit{*} \bnfnt{integer}) \\ +\\ +\bnfnt{instanceof} ::= \\ +\qquad \bnflit{instanceof} \bnfnt{instanceof-location} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* \\ +\\ +\bnfnt{new-location} ::= \\ +\qquad \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\ +\qquad (\bnflit{\#} \bnfnt{integer}) \\ +\qquad \bnfcmt Source code index: the integer is the zero-based index of the object or array \\ +\qquad \bnfcmt creation within the method. \\ +\qquad \bnfor{} (\bnflit{*} \bnfnt{integer}) \\ +\\ +\bnfnt{new} ::= \\ +\qquad \bnflit{new} \bnfnt{new-location} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\\ +\bnfnt{call-location} ::= \\ +\qquad \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\ +\qquad (\bnflit{\#} \bnfnt{integer}) \\ +\qquad \bnfcmt Source code index: the integer is the zero-based index of the method call \\ +\qquad \bnfcmt within the field or method definition. \\ +\qquad \bnfor{} (\bnflit{*} \bnfnt{integer}) \\ +\\ +\bnfnt{call} ::= \\ +\qquad \bnflit{call} \bnfnt{call-location} \bnflit{:} \lineend \\ +\qquad \bnfnt{typearg-definition}* \\ +\\ +\bnfnt{reference-location} ::= \\ +\qquad \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\ +\qquad (\bnflit{\#} \bnfnt{integer}) \\ +\qquad \bnfcmt Source code index: the integer is the zero-based index of the member \\ +\qquad \bnfcmt reference~\cite{JSR308-webpage-201310}. \\ +\qquad \bnfor{} (\bnflit{*} \bnfnt{integer}) \\ +\\ +\bnfnt{reference} ::= \\ +\qquad \bnflit{reference} \bnfnt{reference-location} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* \\ +\qquad \bnfnt{typearg-definition}* \\ +\\ +\bnfnt{lambda-location} ::= \\ +\qquad \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\ +\qquad (\bnflit{\#} \bnfnt{integer}) \\ +\qquad \bnfcmt Source code index: the integer is the zero-based index of the lambda \\ +\qquad \bnfcmt expression~\cite{JSR308-webpage-201310}. \\ +\qquad \bnfor{} (\bnflit{*} \bnfnt{integer}) \\ +\\ +\bnfnt{lambda} ::= \\ +\qquad \bnflit{lambda} \bnfnt{lambda-location} \bnflit{:} \lineend \\ +%\qquad \bnfnt{return-type}? \\ +\qquad \bnfnt{parameter-definition}* \\ +\qquad \bnfnt{variable-definition}* \\ +\qquad \bnfnt{expression-annotations}* +\\ +\qquad \= \kill +\bnfnt{typearg-definition} ::= \\ +\qquad \bnfcmt The integer is the zero-based type argument index. \\ +\qquad \bnflit{typearg} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\subsubsection{AST paths\label{ast-paths}} + +A path through the AST (abstract +syntax tree) specifies an arbitrary expression in source code to modify. +AST paths can be used in the \code{.jaif} file to specify a location to +insert either a bare annotation (\bnflit{insert-annotation}) or a cast +(\bnflit{insert-typecast}). + +For a cast insertion, the \code{.jaif} file specifies the type to cast to. +The annotations on the \bnflit{insert-typecast} line will be inserted on +the outermost type of the type to cast to. If the type to cast to is a compound +type then annotations on parts of the compound type are specified with the +\bnfnt{compound-type} rule. If there are no annotations on +the \bnflit{insert-typecast} line then a cast with no annotations will be +inserted or, if compound type annotations are specified, a cast with annotations +only on the compound types will be inserted. + +Note that the type specified on the \bnflit{insert-typecast} line cannot contain +any qualified type names. For example, use \code{Entry<String, Object>} instead +of \code{Map.Entry<java.lang.String, java.lang.Object>}. + +\begin{tabbing} +\bnfnt{source-insert-typecast} ::= \\ +\qquad \bnfcmt \bnfnt{ast-path} is described below. \\ +\qquad \bnfcmt \bnfnt{type} is the un-annotated type to cast to. \\ +\qquad \bnflit{insert-typecast} \bnfnt{ast-path}\bnflit{:} \bnfnt{type-annotation}* \bnfnt{type} \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + +An AST path represents a traversal through the AST. AST paths can only be +used in \bnfnt{field-definition}s and \bnfnt{method-definition}s. +An AST path starts with the first element under the definition. For +methods this is \code{Block} and for fields this is \code{Variable}. + +An AST path is composed of one or more AST entries, separated by commas. Each +AST entry is composed of a tree kind, a child selector, and an optional +argument. An example AST entry is: + +\begin{verbatim} +Block.statement 1 +\end{verbatim} + +The tree kind is \code{Block}, the child selector is \code{statement} and the +argument is \code{1}. + +The available tree kinds correspond to the Java AST tree nodes (from the package +\code{com.sun.source.tree}), but with ``Tree'' removed from the name. For +example, the class \code{com.sun.source.tree.BlockTree} is represented as +\code{Block}. The child selectors correspond to the method names of the given +Java AST tree node, with ``get'' removed from the beginning of the method name +and the first letter lowercased. In cases where the child selector method +returns a list, the method name is made singular and the AST entry also contains +an argument to select the index of the list to take. For example, the method +\code{com\-.sun\-.source\-.tree\-.Block\-Tree\-.get\-Statements()} is represented as +\code{Block.statement} and requires an argument to select the statement to take. + +The following is an example of an entire AST path: + +\begin{verbatim} +Block.statement 1, Switch.case 1, Case.statement 0, ExpressionStatement.expression, + MethodInvocation.argument 0 +\end{verbatim} + +Since the above example starts with a \code{Block} it belongs in a +\bnfnt{method-definition}. This AST path would select an expression that is in +statement 1 of the method, case 1 of the switch statement, statement 0 of the +case, and argument 0 of a method call (\code{ExpressionStatement} is just a +wrapper around an expression that can also be a statement). + +The following is an example of an annotation file with AST paths used to specify +where to insert casts. + +\begin{verbatim} +package p: +annotation @A: + +class ASTPathExample: + +field a: + insert-typecast Variable.initializer, Binary.rightOperand: @A Integer + +method m()V: + insert-typecast Block.statement 0, Variable.initializer: @A Integer + insert-typecast Block.statement 1, Switch.case 1, Case.statement 0, + ExpressionStatement.expression, MethodInvocation.argument 0: @A Integer +\end{verbatim} + +And the matching source code: + +\begin{verbatim} +package p; + +public class ASTPathExample { + + private int a = 12 + 13; + + public void m() { + int x = 1; + switch (x + 2) { + case 1: + System.out.println(1); + break; + case 2: + System.out.println(2 + x); + break; + default: + System.out.println(-1); + } + } +} +\end{verbatim} + +The following is the output, with the casts inserted. + +\begin{verbatim} +package p; +import p.A; + +public class ASTPathExample { + + private int a = 12 + ((@A Integer) (13)); + + public void m() { + int x = ((@A Integer) (1)); + switch (x + 2) { + case 1: + System.out.println(1); + break; + case 2: + System.out.println(((@A Integer) (2 + x))); + break; + default: + System.out.println(-1); + } + } +} +\end{verbatim} + +Using \code{insert-annotation} instead of \code{insert-typecast} yields +almost the same result --- it also inserts a cast. The sole difference +is the inability to specify the type in the cast expression. If you use +\code{insert-annotation}, then the annotation inserter infers the type, +which is \code{int} in this case. + +Note that a cast can be inserted on any expression, not +just the deepest expression in the AST. For example, a cast could be inserted on +the expression \code{i + j}, the identifier \code{i}, and/or the identifier \code{j}. + +To help create correct AST paths it may be useful to view the AST of a class. +The Checker Framework has a processor to do this. The following command will +output indented AST nodes for the entire input program. + +\begin{verbatim} +javac -processor org.checkerframework.common.util.debug.TreeDebug ASTPathExample.java +\end{verbatim} + +The following is the grammar for AST paths. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{ast-path} ::= \\ +\qquad \bnfnt{ast-entry} [ \bnflit{,} \bnfnt{ast-entry} ]+ \\ +\\ +\bnfnt{ast-entry} ::= \\ +\qquad \bnfnt{annotated-type} \\ +\qquad \bnfor{} \bnfnt{annotation} \\ +\qquad \bnfor{} \bnfnt{array-access} \\ +\qquad \bnfor{} \bnfnt{array-type} \\ +\qquad \bnfor{} \bnfnt{assert} \\ +\qquad \bnfor{} \bnfnt{assignment} \\ +\qquad \bnfor{} \bnfnt{binary} \\ +\qquad \bnfor{} \bnfnt{block} \\ +\qquad \bnfor{} \bnfnt{case} \\ +\qquad \bnfor{} \bnfnt{catch} \\ +\qquad \bnfor{} \bnfnt{compound-assignment} \\ +\qquad \bnfor{} \bnfnt{conditional-expression} \\ +\qquad \bnfor{} \bnfnt{do-while-loop} \\ +\qquad \bnfor{} \bnfnt{enhanced-for-loop} \\ +\qquad \bnfor{} \bnfnt{expression-statement} \\ +\qquad \bnfor{} \bnfnt{for-loop} \\ +\qquad \bnfor{} \bnfnt{if} \\ +\qquad \bnfor{} \bnfnt{instance-of} \\ +\qquad \bnfor{} \bnfnt{intersection-type} \\ +\qquad \bnfor{} \bnfnt{labeled-statement} \\ +\qquad \bnfor{} \bnfnt{lambda-expression} \\ +\qquad \bnfor{} \bnfnt{member-reference} \\ +\qquad \bnfor{} \bnfnt{member-select} \\ +\qquad \bnfor{} \bnfnt{method-invocation} \\ +\qquad \bnfor{} \bnfnt{new-array} \\ +\qquad \bnfor{} \bnfnt{new-class} \\ +\qquad \bnfor{} \bnfnt{parameterized-type} \\ +\qquad \bnfor{} \bnfnt{parenthesized} \\ +\qquad \bnfor{} \bnfnt{return} \\ +\qquad \bnfor{} \bnfnt{switch} \\ +\qquad \bnfor{} \bnfnt{synchronized} \\ +\qquad \bnfor{} \bnfnt{throw} \\ +\qquad \bnfor{} \bnfnt{try} \\ +\qquad \bnfor{} \bnfnt{type-cast} \\ +\qquad \bnfor{} \bnfnt{type-parameter} \\ +\qquad \bnfor{} \bnfnt{unary} \\ +\qquad \bnfor{} \bnfnt{union-type} \\ +\qquad \bnfor{} \bnfnt{variable-type} \\ +\qquad \bnfor{} \bnfnt{while-loop} \\ +\qquad \bnfor{} \bnfnt{wildcard-tree} \\ +\\ +\bnfnt{annotated-type} :: = \\ +\qquad \bnflit{AnnotatedType} \bnflit{.} ( ( \bnflit{annotation} \bnfnt{integer} ) \bnfor{} \bnflit{underlyingType} ) \\ +\\ +\bnfnt{annotation} ::= \\ +\qquad \bnflit{Annotation} \bnflit{.} ( \bnflit{type} \bnfor{} \bnflit{argument} \bnfnt{integer} ) \\ +\\ +\bnfnt{array-access} ::= \\ +\qquad \bnflit{ArrayAccess} \bnflit{.} ( \bnflit{expression} \bnfor{} \bnflit{index} ) \\ +\\ +\bnfnt{array-type} ::= \\ +\qquad \bnflit{ArrayType} \bnflit{.} \bnflit{type} \\ +\\ +\bnfnt{assert} ::= \\ +\qquad \bnflit{Assert} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{detail} ) \\ +\\ +\bnfnt{assignment} ::= \\ +\qquad \bnflit{Assignment} \bnflit{.} ( \bnflit{variable} \bnfor{} \bnflit{expression} ) \\ +\\ +\bnfnt{binary} ::= \\ +\qquad \bnflit{Binary} \bnflit{.} ( \bnflit{leftOperand} \bnfor{} \bnflit{rightOperand} ) \\ +\\ +\bnfnt{block} ::= \\ +\qquad \bnflit{Block} \bnflit{.} \bnflit{statement} \bnfnt{integer} \\ +\\ +\bnfnt{case} ::= \\ +\qquad \bnflit{Case} \bnflit{.} ( \bnflit{expression} \bnfor{} ( \bnflit{statement} \bnfnt{integer} ) ) \\ +\\ +\bnfnt{catch} ::= \\ +\qquad \bnflit{Catch} \bnflit{.} ( \bnflit{parameter} \bnfor{} \bnflit{block} ) \\ +\\ +\bnfnt{compound-assignment} ::= \\ +\qquad \bnflit{CompoundAssignment} \bnflit{.} ( \bnflit{variable} \bnfor{} \bnflit{expression} ) \\ +\\ +\bnfnt{conditional-expression} ::= \\ +\qquad \bnflit{ConditionalExpression} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{trueExpression} \bnfor{} \bnflit{falseExpression} ) \\ +\\ +\bnfnt{do-while-loop} ::= \\ +\qquad \bnflit{DoWhileLoop} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{statement} ) \\ +\\ +\bnfnt{enhanced-for-loop} ::= \\ +\qquad \bnflit{EnhancedForLoop} \bnflit{.} ( \bnflit{variable} \bnfor{} \bnflit{expression} \bnfor{} \bnflit{statement} ) \\ +\\ +\bnfnt{expression-statement} ::= \\ +\qquad \bnflit{ExpressionStatement} \bnflit{.} \bnflit{expression} \\ +\\ +\bnfnt{for-loop} ::= \\ +\qquad \bnflit{ForLoop} \bnflit{.} ( ( \bnflit{initializer} \bnfnt{integer} ) \bnfor{} \bnflit{condition} \bnfor{} ( \bnflit{update} \bnfnt{integer} ) \bnfor{} \bnflit{statement} ) \\ +\\ +\bnfnt{if} ::= \\ +\qquad \bnflit{If} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{thenStatement} \bnfor{} \bnflit{elseStatement} ) \\ +\\ +\bnfnt{instance-of} ::= \\ +\qquad \bnflit{InstanceOf} \bnflit{.} ( \bnflit{expression} \bnfor{} \bnflit{type} ) \\ +\\ +\bnfnt{intersection-type} ::= \\ +\qquad \bnflit{IntersectionType} \bnflit{.} \bnflit{bound} \bnfnt{integer} \\ +\\ +\bnfnt{labeled-statement} ::= \\ +\qquad \bnflit{LabeledStatement} \bnflit{.} \bnflit{statement} \\ +\\ +\bnfnt{lambda-expression} ::= \\ +\qquad \bnflit{LambdaExpression} \bnflit{.} ( ( \bnflit{parameter} \bnfnt{integer} ) \bnfor{} \bnflit{body} ) \\ +\\ +\bnfnt{member-reference} ::= \\ +\qquad \bnflit{MemberReference} \bnflit{.} ( \bnflit{qualifierExpression} \bnfor{} ( \bnflit{typeArgument} \bnfnt{integer} ) ) \\ +\\ +\bnfnt{member-select} ::= \\ +\qquad \bnflit{MemberSelect} \bnflit{.} \bnflit{expression} \\ +\\ +\bnfnt{method-invocation} ::= \\ +\qquad \bnflit{MethodInvocation} \bnflit{.} ( ( \bnflit{typeArgument} \bnfnt{integer} ) \bnfor{} \bnflit{methodSelect} \\ +\qquad \bnfor{} ( \bnflit{argument} \bnfnt{integer} ) ) \\ +\\ +\bnfnt{new-array} ::= \\ +\qquad \bnflit{NewArray} \bnflit{.} ( \bnflit{type} \bnfor{} ( \bnflit{dimension} \bnfor{} \bnflit{initializer} ) \bnfnt{integer} ) \\ +\\ +\bnfnt{new-class} ::= \\ +\qquad \bnflit{NewClass} \bnflit{.} ( \bnflit{enclosingExpression} \bnfor{} ( \bnflit{typeArgument} \bnfnt{integer} ) \bnfor{} \bnflit{identifier} \\ +\qquad \bnfor{} ( \bnflit{argument} \bnfnt{integer} ) \bnfor{} \bnflit{classBody} ) \\ +\\ +\bnfnt{parameterized-type} ::= \\ +\qquad \bnflit{ParameterizedType} \bnflit{.} ( \bnflit{type} \bnfor{} ( \bnflit{typeArgument} \bnfnt{integer} ) ) \\ +\\ +\bnfnt{parenthesized} ::= \\ +\qquad \bnflit{Parenthesized} \bnflit{.} \bnflit{expression} \\ +\\ +\bnfnt{return} ::= \\ +\qquad \bnflit{Return} \bnflit{.} \bnflit{expression} \\ +\\ +\bnfnt{switch} ::= \\ +\qquad \bnflit{Switch} \bnflit{.} ( \bnflit{expression} \bnfor{} ( \bnflit{case} \bnfnt{integer} ) ) \\ +\\ +\bnfnt{synchronized} ::= \\ +\qquad \bnflit{Synchronized} \bnflit{.} ( \bnflit{expression} \bnfor{} \bnflit{block} ) \\ +\\ +\bnfnt{throw} ::= \\ +\qquad \bnflit{Throw} \bnflit{.} \bnflit{expression} \\ +\\ +\bnfnt{try} ::= \\ +\qquad \bnflit{Try} \bnflit{.} ( \bnflit{block} \bnfor{} ( \bnflit{catch} \bnfnt{integer} ) \bnfor{} \bnflit{finallyBlock} \bnfor{} ( \bnflit{resource} \bnfnt{integer} ) ) \\ +\\ +\bnfnt{type-cast} ::= \\ +\qquad \bnflit{TypeCast} \bnflit{.} ( \bnflit{type} \bnfor{} \bnflit{expression} ) \\ +\\ +\bnfnt{type-parameter} ::= \\ +\qquad \bnflit{TypeParameter} \bnflit{.} \bnflit{bound} \bnfnt{integer} \\ +\\ +\bnfnt{unary} ::= \\ +\qquad \bnflit{Unary} \bnflit{.} \bnflit{expression} \\ +\\ +\bnfnt{union-type} ::= \\ +\qquad \bnflit{UnionType} \bnflit{.} \bnflit{typeAlternative} \bnfnt{integer} \\ +\\ +\bnfnt{variable} ::= \\ +\qquad \bnflit{Variable} \bnflit{.} ( \bnflit{type} \bnfor{} \bnflit{initializer} ) \\ +\\ +\bnfnt{while-loop} ::= \\ +\qquad \bnflit{WhileLoop} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{statement} ) \\ +\\ +\bnfnt{wildcard} ::= \\ +\qquad \bnflit{Wildcard} \bnflit{.} \bnflit{bound} \\ +\\ +\end{tabbing} + + +\subsection{Annotations\label{annotations-grammar}} + +This section describes the details of how annotations are defined, how +annotations are used, and the different kinds of annotations in an annotation +file. + + +\subsubsection{Annotation definitions\label{annotation-definitions}} + +An annotation definition describes the annotation's fields and their +types, so that they may be referenced in a compact way throughout the +annotation file. Any annotation that is used in an annotation file +% either on a program element or as a field of another annotation definition. +must be defined before use. +(This requirement makes it impossible to define, in an +annotation file, an annotation that is meta-annotated with itself.) +The two exceptions to this rule are the \code{@java.lang.annotation.Target} and +\code{@java.lang.annotation.Retention} meta-annotations. These meta-annotations +are often used in annotation definitions so for ease of use are they not required to +be defined themselves. +In the annotation file, the annotation definition appears within the +package that defines the annotation. The annotation may be applied to +elements of any package. + +Note that these annotation definitions should not be confused with the +\code{@interface} syntax used in a Java source file to declare an annotation. An +annotation definition in an annotation file is only used internally. An +annotation definition in an annotation file will often mirror an +\code{@interface} annotation declaration in a Java source file in order to use +that annotation in an annotation file. + +% TODO, see https://github.com/typetools/annotation-tools/issues/25 +% The Annotation File Utilities can read annotation definitions from the +% classpath, so it is optional to define them in the annotation file. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{annotation-definition} ::= \\ +\qquad \bnfcmt The \bnfnt{decl-annotation}s are the meta-annotations on this annotation. \\ +\qquad \bnflit{annotation} \bnflit{@}\bnfnt{name} + \bnflit{:} \bnfnt{decl-annotation}* \lineend \\ +\qquad \bnfnt{annotation-field-definition}* \\ +\\ +\bnfnt{annotation-field-definition} ::= \\ +\qquad \bnfnt{annotation-field-type} \bnfnt{name} \lineend \\ +\\ +\bnfnt{annotation-field-type} ::= \\ +\qquad \bnfcmt \bnfnt{primitive-type} is any Java primitive type (\code{int}, \code{boolean}, etc.). \\ +\qquad \bnfcmt These are described in detail in Section~\ref{types-and-values}. \\ +\qquad (\bnfnt{primitive-type} \bnfor{} \bnflit{String} \bnfor{} \bnflit{Class} + \bnfor{} (\bnflit{enum} \bnfnt{name}) \bnfor{} (\bnflit{annotation-field} \bnfnt{name})) \bnflit{[]}? \\ +\qquad \bnfor{} \bnflit{unknown[]} \lineend +\end{tabbing} + + +\subsubsection{Annotation uses\label{annotation-uses}} + +Java SE 8 has two kinds of annotations: ``declaration annotations'' and ``type +annotations''. Declaration annotations can be written only on method formal +parameters and the declarations of packages, classes, methods, fields, and local +variables. Type annotations can be written on any use of a type, and on type +parameter declarations. Type annotations must be meta-annotated +with \code{ElementType.TYPE\_USE} and/or \code{ElementType.TYPE\_PARAMETER}. +These meta-annotations are described in more detail in the JSR 308 +specification~\cite{JSR308-webpage-201310}. + +The previous rules have used two productions for annotation uses in an +annotation file: \bnfnt{decl-annotation} and \bnfnt{type-annotation}. +The \bnfnt{decl-annotation} and \bnfnt{type-annotation} productions use the same +syntax to specify an annotation. These two different rules exist only to show +which type of annotation is valid in a given location. A declaration annotation +must be used where the \bnfnt{decl-annotation} production is used and a type +annotation must be used where the \bnfnt{type-annotation} production is used. + +The syntax for an annotation is the same as in a Java source file. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{decl-annotation} ::= \\ +\qquad \bnfcmt \bnfnt{annotation} must be a declaration annotation. \\ +\qquad \bnfnt{annotation} \\ +\\ +\bnfnt{type-annotation} ::= \\ +\qquad \bnfcmt \bnfnt{annotation} must be a type annotation. \\ +\qquad \bnfnt{annotation} \\ +\\ +\bnfnt{annotation} ::= \\ +\qquad \bnfcmt The name may be the annotation's simple name, unless the file \\ +\qquad \bnfcmt contains definitions for two annotations with the same simple name. \\ +\qquad \bnfcmt In this case, the fully-qualified name of the annotation name is required. \\ +% TODO: +% Perhaps we could add that if a class is in the same package +% as an annotation it may always use the simple name (even if there's another +% annotation with the same simple name in another package)? - MP 06/28 +\qquad \bnflit{@}\bnfnt{name} [ \bnflit{(} \bnfnt{annotation-field} [ \bnflit{,} \bnfnt{annotation-field} ]+ \bnflit{)} ] \\ +\\ +\bnfnt{annotation-field} ::= \\ +\qquad \bnfcmt In Java, if a single-field annotation has a field named \\ +\qquad \bnfcmt ``\code{value}'', then that field name may be elided in uses of the\\ +\qquad \bnfcmt annotation: ``\code{@A(12)}'' rather than ``\code{@A(value=12)}''. \\ +\qquad \bnfcmt The same convention holds in an annotation file. \\ +\qquad \bnfnt{name} \bnflit{=} \bnfnt{value} +\end{tabbing} + +\noindent +Certain Java elements allow both declaration and type annotations (for example, +formal method parameters). For these elements, the \bnfnt{type-annotations} +rule is used to differentiate between the declaration annotations and the type +annotations. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{type-annotations} ::= \\ +\qquad \bnfcmt holds the type annotations, as opposed to the declaration annotations. \\ +\qquad \bnflit{type:} \bnfnt{type-annotation}* \lineend \\ +\qquad \bnfnt{compound-type}* +\end{tabbing} + + +\paragraph{Compound type annotations} + +A compound type is a parameterized, wildcard, array, or nested type. Annotations +may be on any type in a compound type. In order to specify the location of an +annotation within a compound type we use a ``type path''. A +type path is composed one or more pairs of type kind and type argument index. + +\begin{tabbing} +\qquad \= \kill +\bnfnt{type-kind} ::= \\ +\qquad \bnflit{0} \bnfcmt annotation is deeper in this array type \\ +\qquad \bnfor{} \bnflit{1} \bnfcmt annotation is deeper in this nested type \\ +\qquad \bnfor{} \bnflit{2} \bnfcmt annotation is on the bound of this wildcard type argument \\ +\qquad \bnfor{} \bnflit{3} \bnfcmt annotation is on the i'th type argument of this parameterized type \\ +\\ +\bnfnt{type-path} ::= \\ +\qquad \bnfcmt The \bnfnt{integer} is the type argument index. \\ +\qquad \bnfnt{type-kind} \bnflit{,} \bnfnt{integer} [ \bnflit{,} \bnfnt{type-kind} \bnflit{,} \bnfnt{integer} ]* \\ +\\ +\bnfnt{compound-type} ::= \\ +\qquad \bnflit{inner-type} \bnfnt{type-path} \bnflit{:} \bnfnt{annotation}* \lineend +\end{tabbing} + +\noindent +The type argument index used in the \bnfnt{type-path} rule must be \bnflit{0} unless the \bnfnt{type-kind} is +\bnflit{3}. In this case, the type argument index selects which type argument +of a parameterized type to use. + +\urldef\cftp\url|https://checkerframework.org/jsr308/specification/java-annotation-design.html#class-file:ext:type_path| +Type paths are explained in more detail, with many examples to ease +understanding, in Section 3.4 of the JSR 308 Specification.\footnotemark +\footnotetext{\cftp} + + +\section{Example\label{example}} + +Consider the code of Figure~\ref{fig:java-example}. +Figure~\ref{fig:annotation-file-examples} shows two legal annotation files +each of which represents its annotations. + + +\begin{figure} +\begin{verbatim} +package p1; + +import p2.*; // for the annotations @A through @D +import java.util.*; + +public @A(12) class Foo { + + public int bar; // no annotation + private @B List<@C String> baz; + + public Foo(@D("spam") Foo this, @B List<@C String> a) { + @B List<@C String> l = new LinkedList<@C String>(); + l = (@B List<@C String>)l; + } +} +\end{verbatim} +\caption{Example Java code with annotations.} +\label{fig:java-example} +\end{figure} + + +\begin{figure} +\begin{tabular}{|c|c|} +\hline +\begin{minipage}[t]{.5\textwidth} +\begin{verbatim} +package p2: +annotation @A: + int value +annotation @B: +annotation @C: +annotation @D: + String value + +package p1: +class Foo: @A(value=12) + + field bar: + + field baz: @B + inner-type 0: @C + + method <init>( + Ljava/util/List;)V: + parameter 0: @B + inner-type 0: @C + receiver: @D(value="spam") + local 1 #3+5: @B + inner-type 0: @C + typecast #7: @B + inner-type 0: @C + new #0: + inner-type 0: @C +\end{verbatim} +\end{minipage} +& +\begin{minipage}[t]{.45\textwidth} +\begin{verbatim} +package p2: +annotation @A + int value + +package p2: +annotation @B + +package p2: +annotation @C + +package p2: +annotation @D + String value + +package p1: +class Foo: @A(value=12) + +package p1: +class Foo: + field baz: @B + +package p1: +class Foo: + field baz: + inner-type 0: @C + +// ... definitions for p1.Foo.<init> +// omitted for brevity +\end{verbatim} +\end{minipage} +\\ +\hline +\end{tabular} + +\caption{Two distinct annotation files each corresponding to the code of + Figure~\ref{fig:java-example}.} +\label{fig:annotation-file-examples} +\end{figure} + + +\section{Types and values\label{types-and-values}} + +The Java language permits several types for annotation fields: primitives, +\code{String}s, \code{java.lang.Class} tokens (possibly parameterized), +enumeration constants, annotations, and one-dimensional arrays of these. + +These \textbf{types} are represented in an annotation file as follows: +\begin{itemize} +\item Primitive: the name of the primitive type, such as \code{boolean}. +\item String: \code{String}. +\item Class token: \code{Class}; the parameterization, if any, is not +represented in annotation files. +\item Enumeration constant: \code{enum} followed by the binary name of +the enumeration class, such as \code{enum java.lang.Thread\$State}. +\item Annotation: \code{@} followed by the binary name of the annotation type. +\item Array: The representation of the element type followed by \code{[]}, such +as \code{String[]}, with one exception: an annotation definition may specify +a field type as \code{unknown[]} if, in all occurrences of that annotation in +the annotation file, the field value is a zero-length array.\footnotemark +\footnotetext{There is a design flaw in the format of array field values in a +class file. An array does not itself specify an element type; instead, each +element specifies its type. If the annotation type \code{X} has an array field +\code{arr} but \code{arr} is zero-length in every \code{@X} annotation in the +class file, there is no way to determine the element type of \code{arr} from the +class file. This exception makes it possible to define \code{X} when the class +file is converted to an annotation file.} +\end{itemize} + +Annotation field \textbf{values} are represented in an annotation file as follows: +\begin{itemize} +\item Numeric primitive value: literals as they would appear in Java source +code. +\item Boolean: \code{true} or \code{false}. +\item Character: A single character or escape sequence in single quotes, such +as \code{'A'} or \code{'\char`\\''}. +\item String: A string literal as it would appear in source code, such as +\code{"\char`\\"Yields falsehood when quined\char`\\" yields falsehood when quined."}. +\item Class token: The binary name of the class (using \code{\$} for +inner classes) or the name of the primitive type or \code{void}, possibly +followed by \code{[]}s representing array layers, followed by \code{.class}. +Examples: \code{java.lang.Integer[].class}, \code{java.util.Map\$Entry.class}, +and \code{int.class}. +\item Enumeration constant: the name of the enumeration constant, such as +\code{RUNNABLE}. +\item Array: a sequence of elements inside \code{\char`\{\char`\}} with a comma +between each pair of adjacent elements; a comma following the last element is +optional as in Java. Also as in Java, the braces may be omitted if the +array has only one element. +Examples: \code{\char`\{1\char`\}}, \code{1}, +\code{\char`\{true, false,\char`\}} and \code{\char`\{\char`\}}. +\end{itemize} + +The following example annotation file shows how types and values are represented. + +\begin{verbatim} +package p1: + +annotation @ClassInfo: + String remark + Class favoriteClass + Class favoriteCollection // it's probably Class<? extends Collection> + // in source, but no parameterization here + char favoriteLetter + boolean isBuggy + enum p1.DebugCategory[] defaultDebugCategories + @p1.CommitInfo lastCommit + +annotation @CommitInfo: + byte[] hashCode + int unixTime + String author + String message + +class Foo: @p1.ClassInfo( + remark="Anything named \"Foo\" is bound to be good!", + favoriteClass=java.lang.reflect.Proxy.class, + favoriteCollection=java.util.LinkedHashSet.class, + favoriteLetter='F', + isBuggy=true, + defaultDebugCategories={DEBUG_TRAVERSAL, DEBUG_STORES, DEBUG_IO}, + lastCommit=@p1.CommitInfo( + hashCode={31, 41, 59, 26, 53, 58, 97, 92, 32, 38, 46, 26, 43, 38, 32, 79}, + unixTime=1152109350, + author="Joe Programmer", + message="First implementation of Foo" + ) +) +\end{verbatim} + + +\section{Alternative formats\label{alternative-formats}} + +We mention multiple alternatives to the format described in this document. +Each of them has its own merits. +In the future, the other formats could be implemented, along with tools for +converting among them. +% Then, we can see which of the formats programmers prefer in practice. + + + +An alternative to the format described in this document would be XML\@. +% It would be easy to use an XML format to augment the one proposed here, but +XML does not seem to provide any compelling advantages. Programmers +interact with annotation files in two ways: textually (when reading, writing, +and editing annotation files) and programmatically (when writing +annotation-processing tools). Textually, XML can be +very hard to read; style sheets mitigate this +problem, but editing XML files remains tedious and error-prone. +Programmatically, a layer of abstraction (an API) is needed in any event, so it +makes little difference what the underlying textual representation is. +XML files are easier to parse, but the parsing code only needs to be +written once and is abstracted away by an API to the data structure. + + +Another alternative is a format like the \code{.spec}/\code{.jml} files +of JML~\cite{LeavensBR2006:JML}. The format is similar to Java code, but +all method bodies are empty, and users can annotate the public members of a +class. This is easy for Java programmers to read and understand. (It is a +bit more complex to implement, but that is not particularly germane.) +Because it does not permit complete specification of a class's annotations +(it does not permit annotation of method bodies), it is not appropriate for +certain tools, such as type inference tools. However, it might be desirable +to adopt such a format for public members, and to use the format +described in this document primarily for method bodies. + + +The Checker Framework~\cite{DietlDEMS2011,CF} uses two additional formats for +annotations. The first format is called ``stub files.'' A stub file is similar +to the \code{.spec}/\code{.jml} files described in the previous paragraph. It +uses Java syntax, only allows annotations on method headers and does not require +method bodies. A stub file is used to add annotations to method headers of +existing Java classes. For example, the Checker Framework uses stub files to add +annotations to method headers of libraries (such as the JDK) without modifying +the source code or bytecode of the library. A single stub file can contain +multiple packages and classes. This format only allows annotations on method +headers, not class headers, fields, and method bodies like in a \code{.jaif} +file. Further, stub files are only used by the Checker Framework at run time, +they cannot be used to insert annotations into a source or classfile. + + +The Checker Framework also uses a format called an ``annotated JDK.'' The +annotated JDK is a \code{.jar} file containing the JDK with annotations. It is +created with the Annotation File Utilities, but the annotations are stored in a +format similar to a stub file, instead of in a \code{.jaif} file. The annotated +JDK starts with a source file for each file in the JDK to be annotated. Like a +stub file, each source file only contains method headers with annotations. The +annotated JDK also supports annotations in the class header. To build the +annotated JDK \code{.jar} file, the source files are compiled, then the +\code{extract-annotations} script is run on them to generate a \code{.jaif} file +for each source file. The \code{insert-annotations} script then inserts the +annotations contained in each \code{.jaif} file into the corresponding JDK class +file. These are then packaged up into a single \code{.jar} file. Like a stub +files, the annotated JDK is easier to read and write since it uses Java syntax. +However, the annotated JDK requires a different file for each original Java +source file. It does not allow annotations on fields and in method bodies. The +annotated JDK also only contains annotations in the JDK and not other Java +files. + + + +\bibliographystyle{alpha} +\bibliography{annotation-file-format,bibstring-unabbrev,types,ernst,invariants,generals,alias,crossrefs} + +\end{document} + +% LocalWords: java javac OuterClass InnerClass TODO Kleene MP subannotations +% LocalWords: enum arr quined int pt instanceof RUNTIME JVML ILjava boolean +% LocalWords: programmatically jml ernst jaif whitespace 0pt decl enums +% LocalWords: filenames typeparam javap init clinit ast un lowercased io +% LocalWords: ExpressionStatement AnnotatedType underlyingType ArrayType +% LocalWords: ArrayAccess leftOperand rightOperand CompoundAssignment +% LocalWords: ConditionalExpression trueExpression falseExpression +% LocalWords: DoWhileLoop EnhancedForLoop ForLoop thenStatement NewArray +% LocalWords: elseStatement InstanceOf LabeledStatement LambdaExpression +% LocalWords: MemberReference qualifierExpression typeArgument NewClass +% LocalWords: MemberSelect MethodInvocation methodSelect classBody +% LocalWords: enclosingExpression ParameterizedType finallyBlock AScene +% LocalWords: TypeCast UnionType typeAlternative WhileLoop ElementType +% LocalWords: AClass AMethod AElement objectweb anno tations parseScene +% LocalWords: CriterionList isSatisifiedBy CriteriaList afu getPositions +% LocalWords: InPackageCriterion InClassCriterion InMethodCriterion +% LocalWords: ParamCriterion inserter RUNNABLE ASM src asm
diff --git a/annotation-file-utilities/annotation-file-utilities.html b/annotation-file-utilities/annotation-file-utilities.html new file mode 100644 index 0000000..8bd8874 --- /dev/null +++ b/annotation-file-utilities/annotation-file-utilities.html
@@ -0,0 +1,642 @@ +<!DOCTYPE html> +<html> +<head> + <title>Annotation File Utilities</title> +</head> +<body> +<h1>Annotation File Utilities</h1> + +<p>Contents:</p> +<!-- start toc. do not edit; run html-update-toc instead --> + <ul> + <li><a href="#motivation">Motivation</a> + <ul> + <li><a href="#jaif-file">External storage of annotations</a></li> + <li><a href="#annotation-file-utilities-description">Annotation File Utilities</a></li> + </ul></li> + <li><a href="#installation">Installation</a> + <ul> + <li><a href="#viewing-source">Building from source</a></li> + </ul></li> + <li><a href="#using">Using the Annotation File Utilities</a> + <ul> + <li><a href="#insert-annotations">Insert-annotations</a></li> + <li><a href="#extract-annotations">Extract-annotations</a></li> + <li><a href="#insert-annotations-to-source">Insert-annotations-to-source</a></li> + </ul></li> + <li><a href="#implementation">Design and Implementation Details</a> + <ul> + <li><a href="#asmx">Asmx</a></li> + <li><a href="#scene-lib">Scene-lib</a> + <ul> + <li><a href="#bytecode-insertion">Bytecode Insertion</a></li> + <li><a href="#bytecode-extraction">Bytecode Extraction</a></li> + </ul></li> + <li><a href="#source-insertion">Annotation-file-utilities</a></li> + </ul></li> + <li><a href="#feedback">Feedback and bug reports</a> + <ul> + <li><a href="#changelog">Changelog</a></li> + </ul></li> + </ul> +<!-- end toc --> + + + +<hr /> +<h2 id="motivation">Motivation</h2> + +<p> +Java annotations are meta-data about Java program elements, as in +“<code><mark>@Deprecated</mark> class Date +{ ... }</code>” or “<code>List<<mark>@NonNull</mark> String></code>”. +Ordinarily, Java annotations are +written in the +source code of a <code>.java</code> Java source file. When +<code>javac</code> compiles the source code, it inserts the annotations in +the resulting <code>.class</code> file (as +“attributes”). +</p> + +<!-- + I've moved text from here to the annotation-file-format document, but feel + free to improve that document. +--> + +<h3 id="jaif-file">External storage of annotations</h3> + +<p> +Sometimes, it is convenient to specify +the annotations outside the source code or the <code>.class</code> file. +The document +“Annotation File Format Specification” (<a + href="annotation-file-format.pdf">PDF</a>, <a + href="annotation-file-format.html">HTML</a>) +defines a textual format for annotations, +and it also motivates reasons why such a file format is necessary in +addition to the <code>.java</code> and <code>.class</code> formats. The +file format supports both the declaration annotations and type annotations. +</p> + +<p> +An annotation file +conventionally has the extension <code>.jaif</code> (for Java Annotation Index +File). +The <a href="#scene-lib"><code>scene-lib</code></a> sub-project provides +API methods for building and manipulating annotation files. +</p> + + +<h3 id="annotation-file-utilities-description">Annotation File Utilities</h3> + +<p> +Programmers need to be able to transfer annotations +between the three possible locations for annotations — source code, class files, +and annotation files. Programmers will want to extract +annotations from source and class files to an annotation file in order to easily +read annotations, while various tools will only read annotations from +source and class files. The Annotation File Utilities provide three tools +to read and write annotation files. +</p> + +<ul> + <li> <a href="#insert-annotations"><code>insert-annotations</code></a> reads annotations from an annotation file + and inserts them into a class file</li> + <li> <a href="#extract-annotations"><code>extract-annotations</code></a> reads annotations from a class file + and writes them out to an annotation file</li> + <li> <a href="#insert-annotations-to-source"><code>insert-annotations-to-source</code></a> reads annotations from an + annotation file and inserts them into a Java source file</li> +</ul> + +<p> +A diagram showing the interactions betweeen these tools is below. +</p> + +<div> +<img src="figures/tool-relations.svg" alt="Relationships between AFU tools" /> +</div> + +<p> +There is no +<code>extract-annotations-from-source</code> tool: one can +compile the source code and then use +<code>extract-annotations</code> to read the annotations from the class +file. +</p> + + +<hr /> +<h2 id="installation">Installation</h2> + +<p> +The following instructions assume either a Linux or Windows system using a command-line environment. +</p> + +<p> +The current release is Annotation File Utilities version +<!-- afu-version -->3.6.44, 03 Aug 2017<!-- /afu-version -->. +</p> + +<ol> +<li> + Download + <a href="annotation-tools-3.6.44.zip"><!-- annotation-tools-zip-version -->annotation-tools-3.6.44.zip<!-- /annotation-tools-zip-version --></a>. +</li> + +<li> + Creade a directory named + <code>annotation-tools</code> by + unpacking the distribution zipfile (a standard place to do this is in a + directory <code>~/jsr308/</code>): + + <pre><code>unzip annotation-tools-3.6.44.zip</code></pre> + + The <code>annotation-tools</code> directory must be a sibling of the <code>jsr308-langtools</code> + directory (available at <a href="https://checkerframework.org/jsr308/">https://checkerframework.org/jsr308/</a>). + Alternatively, Unix (including Linux and MacOS) users may set the <code>LANGTOOLS</code> environment variable to + the location of the <code>jsr308-langtools</code> directory: + + <pre><code>export LANGTOOLS=<em>/path/to</em>/jsr308-langtools</code></pre> +</li> + +<li> +Add the <code>annotation-file-utilities</code> directory to your path. + +<ul> +<li> +For <b>Unix</b> (including Linux and MacOS), add the directory to your PATH +environment variable. If your shell is sh or bash, add to your +<code>~/.bashrc</code> or <code>~/.bash_profile</code> file: +<pre><code>export PATH=${PATH}:<em>/path/to</em>/annotation-tools/annotation-file-utilities/scripts</code></pre> +<!-- Omitted to save space and simplify instructions +For csh/tcsh, add to ~/.tcshrc or ~/.cshrc or ~/.login: +<pre><code>setenv PATH=${PATH}:/path/to/annotation-file-utilities/</code></pre> +--> +</li> +<li> +For <b>Windows</b>, add the directory to your +<code>PATH</code> system +variable by going to + +<pre><code> Control Panel -> System -> Advanced -> Environment Variables </code></pre> + +From there, find the <code>PATH</code> variable under “System variables” +and append to it the directory <code><em>path\to</em>\annotatation-tools\annotation-file-utilities\scripts</code>. +</li> +</ul> +</li> +</ol> + + +<!-- I think this is obvious, so I have commented it out -MDE. --> +<!-- +<p> +To update the annotation file utilities, simply download the most recent <code>annotation-tools.zip</code> file from this website and replace the existing <code>annotation-tools.zip</code>, then extract the file just as when you first installed it. As long as you followed the above instructions, no further work needs to be done. +</p> +--> + +<h3 id="viewing-source">Building from source</h3> + +<p> +The annotation file utilities are pre-compiled (a jar file is included in +the distribution), so most users do not need to compile it themselves. +</p> + +<p> +There are two ways to obtain the source code. +Source code is provided in the +<a href="https://github.com/typetools/annotation-tools/releases">distribution</a>. +Alternately, see the source code repository at +<a href="https://github.com/typetools/annotation-tools">https://github.com/typetools/annotation-tools</a>. +</p> + +<p> +To compile, run +<code>ant jarfile</code> from the <code>annotation-file-utilities</code> +subdirectory. If the <code>javac</code> and <code>java</code> programs are +not on your execution path, see the notes near the top of +<code>annotation-file-utilities/tests/Makefile</code>. +</p> + + +<hr /> +<h2 id="using">Using the Annotation File Utilities</h2> + +<p> +To use the tools, simply run them from the command-line with the +appropriate arguments. The following instructions are for running the +tools on a Linux/Unix/MacOS machine. +The tools work identically on Windows, except +the extension <code>.bat</code> needs to be appended to the tool name (for +example, Windows users would execute <code>insert-annotations.bat</code> +instead of <code>insert-annotations</code>). +</p> + +<p> +For all the tools, arguments starting with a single +‘<code>@</code>’ are recognized as argument files +(<code>argfiles</code>), the contents of which get expanded into the +command line. (Initial <code>@@</code> represents a literal +<code>@</code> in the argument text.) For additional details of argfile +processing, refer to Oracle's +<a href="https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html"><code>javac</code></a> +documentation. +</p> + +<h3 id="insert-annotations"> Insert-annotations </h3> + +<p> +To insert annotations specified by an annotation file into a class file, use the +insert-annotations tool. Running: +</p> + +<pre><code>insert-annotations mypackage.MyClass indexFile.jaif</code></pre> + +<p> +will read in all the annotations from the annotation file +<code>indexFile.jaif</code> and insert those annotations pertaining to +<code>mypackage.myClass</code> into the class file for +<code>mypackage.MyClass</code>, outputting the final class file to +<code>mypackage.MyClass.class</code> in the present working directory. +Note that the class file for <code>mypackage.MyClass</code> must be located +on your classpath. +</p> + +<p> +Multiple pairs of class and index files (in that order) can be specified +on a single command line; if the program exits normally, the results +are the same as if the program were run once for each pair of arguments +in sequence. +Run: +</p> + +<pre><code>insert-annotations --help</code></pre> + +<p> +for full usage information. +</p> + + +<h3 id="extract-annotations"> Extract-annotations </h3> + +<p> +To extract annotations from a class file and write them to an annotation file, +use the extract-annotations tool. Running: +</p> + +<pre><code>extract-annotations mypackage.MyClass</code></pre> + +<p> +will locate the class file for <code>mypackage.MyClass</code>, read all +annotations from it, and write the results in annotation file format to +<code>mypackage.MyClass.jaif</code> in the present working directory. Note +that <code>mypackage.MyClass</code> must be located on your classpath. +</p> + +<p> +Multiple classes can be specified on a single command line; if the +program exits normally, the results are the same as if the program were +run once for each class in sequence. +Run: +</p> + +<pre><code>extract-annotations --help</code></pre> + +<p> +for full usage information. +</p> + + +<h3 id="insert-annotations-to-source">Insert-annotations-to-source </h3> + +<p> +To insert annotations specified by an annotation file into a Java source file, +use the insert-annotations-to-source tool. Running: +</p> + +<pre><code>insert-annotations-to-source index1.jaif index2.jaif mypackage/MyClass.java yourpackage/YourClass.java</code></pre> + +<p> +will read all the annotations from <code>index1.jaif</code> and +<code>index2.jaif</code>, insert them (when applicable) into their +appropriate locations in <code>mypackage/MyClass.java</code> and +<code>yourpackage/YourClass.java</code>, and write the results to +<code>annotated/mypackage/MyClass.java</code> and +<code>annotated/mypackage/MyClass.java</code>, respectively. +</p> + +<p> +Index and source files can be specified in any order, mixing the two +file types freely; if the source files have no overlapping definitions +and the program exits normally, the results are the same as if the +program were run once for each source file, with <em>all</em> JAIFs +given for each run. +Run: +</p> + +<pre><code>insert-annotations-to-source --help</code></pre> + +<p> +for full usage information. +</p> + +<p> +If you wish to insert annotations into method bodies, you must have the +associated class <code>mypackage.MyClass.class</code> on your classpath. +You can insert annotations on class/field/method declarations and +signatures without the class on your classpath. +</p> + +<p> +In general (but see below for exceptions), your source code needs to +contain the locations that your +<code>.jaif</code> file references. In particular, if the +<code>.jaif</code> file contains annotations for a type parameter, but the +source code uses a raw type, then you will get an error such as +</p> +<pre> +Found class Edge, but unable to insert @checkers.nullness.quals.Nullable: + @checkers.nullness.quals.Nullable (nl=true) @ [GenericArrayLocationCriterion at ( [TYPE_ARGUMENT(0)] ), ... +</pre> +<p> +In this case, you should add type arguments, such as changing +</p> +<pre> public void pushNonezeroRing(Stack stack, Hashtable seen) {</pre> +<p>to</p> +<pre> public void pushNonezeroRing(Stack<Edge> stack, Hashtable<Edge, ?> seen) {</pre> +<p> +In a few cases, insert-annotations-to-source will generate "default" +code to provide a location for an annotation. These include +</p> +<ul> +<li>method and constructor <a href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.1-220">receivers;</a></li> +<li>type parameter bounds (<code>extends Object</code>);</li> +<li>type casts for expressions</li> +<li>nullary constructor definitions; and</li> +<li>explicit constructor invocations for literal arrays (e.g., <code>{"a", "b"}</code> becomes <code>new String[] {"a", "b"}</code>).</li> +</ul> + +<p> +The command-line options are as follows: +</p> + +<!-- start options doc (DO NOT EDIT BY HAND) --> +<ul> + <li id="optiongroup:General-options">General options + <ul> + <li id="option:outdir"><b>-d</b> <b>--outdir=</b><i>directory</i>. Directory in which output files are written. [default annotated/]</li> + <li id="option:in-place"><b>-i</b> <b>--in-place=</b><i>boolean</i>. If true, overwrite original source files (making a backup first). + Furthermore, if the backup files already exist, they are used instead + of the .java files. This behavior permits a user to tweak the .jaif + file and re-run the annotator. + <p> + + Note that if the user runs the annotator with --in-place, makes edits, + and then re-runs the annotator with this --in-place option, those + edits are lost. Similarly, if the user runs the annotator twice in a + row with --in-place, only the last set of annotations will appear in + the codebase at the end. + <p> + + To preserve changes when using the --in-place option, first remove the + backup files. Or, use the <code>-d .</code> option, which makes (and + reads) no backup, instead of --in-place. [default false]</li> + <li id="option:abbreviate"><b>-a</b> <b>--abbreviate=</b><i>boolean</i>. Abbreviate annotation names [default true]</li> + <li id="option:comments"><b>-c</b> <b>--comments=</b><i>boolean</i>. Insert annotations in comments [default false]</li> + <li id="option:omit-annotation"><b>-o</b> <b>--omit-annotation=</b><i>string</i>. Omit given annotation</li> + <li id="option:nowarn"><b>--nowarn=</b><i>boolean</i>. Suppress warnings about disallowed insertions [default false]</li> + <li id="option:convert-jaifs"><b>--convert-jaifs=</b><i>boolean</i>. Convert JAIFs to new format [default false]</li> + <li id="option:help"><b>-h</b> <b>--help=</b><i>boolean</i>. Print usage information and exit [default false]</li> + </ul> + </li> + <li id="optiongroup:Debugging-options">Debugging options + <ul> + <li id="option:verbose"><b>-v</b> <b>--verbose=</b><i>boolean</i>. Verbose (print progress information) [default false]</li> + <li id="option:debug"><b>--debug=</b><i>boolean</i>. Debug (print debug information) [default false]</li> + <li id="option:print-error-stack"><b>--print-error-stack=</b><i>boolean</i>. Print error stack [default false]</li> + </ul> + </li> +</ul> +<!-- end options doc --> + + +<hr /> +<h2 id="implementation">Design and Implementation Details</h2> + +<p>This section describes some high level-design and implementation +details of the Annotation File Utilities, including the different +components of the Annotation File Utilities and how they fit together. +It is intended for someone who is beginning work on the Annotation File +Utilities or is curious about how the Annotation File Utilities work.</p> + +<p>The Annotation File Utilities is composed of three sub-projects: +<code>asmx</code>, <code>scene-lib</code>, and +<code>annotation-file-utilities</code>. The <code>asmx</code> +sub-project provides methods for reading and writing class files. The +<code>scene-lib</code> sub-project represents a <code>.jaif</code> file +and inserts and extracts annotations to/from bytecode. The +<code>annotation-file-utilities</code> sub-project inserts annotations +into source code.</p> + +<h3 id="asmx" class="subsection">Asmx</h3> + +<p>The <code>asmx</code> sub-project provides methods for reading and +writing class files. <code>asmx</code> is based on an old version +(2.2.2) of the <a href="http://asm.ow2.org/">ASM Framework</a>. It has been modified to +allow it to read and write <a href="https://checkerframework.org/jsr308/specification/java-annotation-design.html">JSR 308</a> +annotations in bytecode. However, it is far behind the current ASM +version (5.0), which provides support for type annotations. We should +discard the custom <code>asmx</code> in the Annotation File Utilities +and adapt the Annotation File Utilities to use the official, supported +version of ASM.</p> + +<h3 id="scene-lib" class="subsection">Scene-lib</h3> + +<p><code>scene-lib</code> is an interface to a <code>.jaif</code> file. +It reads in and writes out <code>.jaif</code> files and provides an +internal representation of a <code>.jaif</code> file to access and +manipulate.</p> + +<p>Internally, a <code>.jaif</code> file is represented by the +<code>scene-lib/src/annotations/el/AScene.java</code> class. The +<code>AScene</code> class (or “annotated scene”) roughly +parallels the root of an abstract syntax tree. An <code>AScene</code> +has a number of classes (<code>AClass</code>) as children. Each class +has a number of methods (<code>AMethod</code>), fields +(<code>AElement</code>), etc. as children. All of these classes are +related in the type hierarchy shown below.</p> + +<div> +<img src="figures/scene-lib-type-hierarchy.svg" alt="scene-lib type hierarchy"> +</div> + +<p>Each class in the type hierarchy has one or more fields to hold +annotations for the different components of the class. For example, the +<code>AMethod</code> class has the following fields: bounds, return +type, receiver parameters, and throws clause. Each of these fields +holds the annotations stored on that part of the method. For details on +the remainder of the classes in the type hierarchy, and their +respective fields, see the documentation for each file in +<code>scene-lib/src/annotations/el/</code>.</p> + +<p>An <code>AScene</code> instance can be created in two ways. An empty +<code>AScene</code> can be created by calling the <code>AScene</code> +constructor, or an <code>AScene</code> can be created by parsing an +existing <code>.jaif</code> file. Once an <code>AScene</code> is +created, annotations can be added to it by adding them to the correct +fields of the children. An <code>AScene</code> can also be output to +create a new <code>.jaif</code> file.</p> + +<h4 id="bytecode-insertion" class="subsubsection">Bytecode Insertion</h4> + +<p>Annotations can be inserted into bytecode by executing the +<code>annotation-file-utilities/scripts/insert-annotations</code> +script. This script takes one or more ⟨class name, +<code>.jaif</code> file⟩ pairs as arguments. The annotations +specified in the <code>.jaif</code> file are inserted into the +classfile directly before the <code>.jaif</code> file in the argument +list.</p><p>First, each <code>.jaif</code> file is parsed into an +<code>AScene</code> (as described in +<a href="#scene-lib">Scene-lib</a>). Then, +<code>asmx/src/org/objectweb/asm/ClassReader.java</code> parses the +classfile. As it is parsing the classfile, it passes the parsed +bytecode off to the +<code>scene-lib/src/annotations/io/classfile/ClassAnnotationSceneWriter.java</code> +class. This class has a reference to the <code>AScene</code> parsed +from the <code>.jaif</code> file. As this class receives the parsed +bytecode it inserts the relevant annotations from the +<code>AScene</code> in the bytecode and then writes the bytecode back +out.</p> + +<h4 id="bytecode-extraction" class="subsubsection">Bytecode Extraction</h4> + +<p>Annotations can be extracted from bytecode by executing the +<code>annotation-file-utilities/scripts/extract-annotations</code> +script. This script takes one or more class names as arguments and +outputs the annotations found in those classes to <code>.jaif</code> +files.</p><p>First, an empty <code>AScene</code> is constructed to store +the annotations. +<code>asmx/src/org/objectweb/asm/ClassReader.java</code> parses the +classfile and passes the parsed bytecode off to the +<code>scene-lib/src/annotations/io/classfile/ClassAnnotationSceneReader.java</code> +class. This class filters out the annotations in the bytecode and adds +them to the correct part of the <code>AScene</code>. After this, the +<code>AScene</code> is output to a <code>.jaif</code> file.</p> + +<h3 id="source-insertion" class="subsection">Annotation-file-utilities</h3> + +<p>The <code>annotation-file-utilities</code> sub-project inserts annotations into source +code. It can be run by executing the +<code>annotation-file-utilities/scripts/insert-annotations-to-source</code> +script. The script takes one or more <code>.jaif</code> files, followed by one or more <code>.java</code> source files as arguments. The annotations in the <code>.jaif</code> files are inserted into the <code>.java</code> source files.</p><p>First, an instance of +<code>annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java</code> +is created. Its <code>parse</code> method parses the <code>.jaif</code> +file into an <code>AScene</code> (as described in +<a href="#scene-lib">Scene-lib</a>). The <code>parse</code> method +calls the <code>parseScene</code> method, which traverses through the +<code>AScene</code> and creates an +<code>annotation-file-utilities/src/annotator/specification/CriterionList.java.</code> +A <code>CriterionList</code> identifies a unique AST node that is the +location of an insertion. It contains objects that implement the +<code>annotation-file-utilities/src/annotator/find/Criterion.java</code> +interface. +Each <code>Criterion</code> has an <code>isSatisifiedBy</code> method — +a predicate that takes an AST node and returns <code>true</code> if the +AST node satisfies the <code>Criterion</code> and <code>false</code> +otherwise. To determine if a given node matches a +<code>CriterionList</code>, the node is passed to all of the +<code>Criterion</code>s in the <code>CriteriaList</code>. If every +<code>Criterion</code> returns <code>true</code> then it is match. If +one or more <code>Criterion</code>s return <code>false</code> then it is +not a match. The various <code>Criterion</code> classes are in the +<code>annotation-file-utilities/src/annotator/find/</code> directory. +For example, take the following source code:</p> + +<pre class="verbatim">package afu.example; + +public class Test { + public void m(boolean b, int i) { + // ... + } +} +</pre> + +<p>The <code>CriterionList</code> to specify the location of the +<code>i</code> parameter contains the following +<code>Criterion</code>s:</p> + +<ul class="itemize"><li class="li-itemize"> +<code>InPackageCriterion("afu.example")</code> +</li><li class="li-itemize"><code>InClassCriterion("Test")</code> +</li><li class="li-itemize"><code>InMethodCriterion("m(ZI)V")</code> +</li><li class="li-itemize"><code>ParamCriterion(1)</code> +</li></ul> + +<p>After this <code>CriterionList</code> is built up an +<code>annotation-file-utilities/src/annotator/find/Insertion.java</code> +is created. An <code>Insertion</code> stores an +<code>annotation-file-utilities/src/annotator/find/Criteria.java</code> +(which is created from a <code>CriterionList</code>) and the text to be +inserted. All of these <code>Insertion</code>s are then added to a +list. The Java compiler then is called to parse the Java source into an +abstract syntax tree. This is followed by a call to the +<code>getPositions</code> method of +<code>annotation-file-utilities/src/annotator/find/TreeFinder.java</code>, +which scans through each node of the abstract syntax trees. For each node, +it runs through the <code>Criteria</code> for each un-matched +<code>Insertion</code>. If at least one of the <code>Criteria</code> +does not match, then this is not the correct place for the +<code>Insertion</code> and the <code>Insertion</code> will be checked +at the remaining nodes of the tree. If all of the <code>Criteria</code> +match, then this node is the correct place for the +<code>Insertion</code>. It is removed from the list of un-matched +<code>Insertion</code>s and the position where to insert the +<code>Insertion</code> is determined. This position is the integer +index in the file where the <code>Insertion</code> should be inserted. +After the positions are found for all of the <code>Insertion</code>s, +the <code>Insertion</code> text is inserted into the file. This happens +backwards, with <code>Insertion</code>s at the end of the file (i.e. +with higher positions) being inserted first. If <code>Insertion</code>s +were instead inserted from the beginning of the file then a single +<code>Insertion</code> would invalidate all of the positions for the +following <code>Insertion</code>s.</p> + +<p>If there are remaining <code>Insertion</code>s that were not matched +to a node in the abstract syntax tree then an error message is +displayed.</p> + + +<hr /> +<h2 id="feedback"> Feedback and bug reports </h2> + +<p> +To submit a bug report or request a new feature, use the +<a href="https://github.com/typetools/annotation-tools/issues">issue + tracker</a>. When reporting a bug, please include exact instructions in +how to reproduce it, and please also attach relevant input files. This +will let us resolve the issue quickly. +</p> + +<p> +You can also reach the developers at +<a href="mailto:annotation-tools-dev@googlegroups.com">annotation-tools-dev@googlegroups.com</a>. +But please use the + <a href="https://github.com/typetools/annotation-tools/issues">issue + tracker</a> for bug reports and feature requests. +</p> + + +<h3 id="changelog">Changelog</h3> + +<p> +The <a href="changelog.html">changelog</a> describes what is new in each release. +</p> + + +<hr/> + +</body> +</html> + +<!-- LocalWords: utils bashrc tcsh tcshrc cshrc classpath + -->
diff --git a/annotation-file-utilities/annotation-file-utilities.jar b/annotation-file-utilities/annotation-file-utilities.jar new file mode 100644 index 0000000..8d1e8f0 --- /dev/null +++ b/annotation-file-utilities/annotation-file-utilities.jar Binary files differ
diff --git a/annotation-file-utilities/build.properties b/annotation-file-utilities/build.properties new file mode 100644 index 0000000..bf00a31 --- /dev/null +++ b/annotation-file-utilities/build.properties
@@ -0,0 +1,10 @@ +workspace : ${basedir}/../.. +global.build.properties : ${basedir}/../global.build.properties +user.build.properties : ${basedir}/../user.build.properties +afu.ver.0=<!-- afu-version --> +afu.ver.1=<!-- /afu-version --> +afu.date.0=<!-- afu-date --> +afu.date.1=<!-- /afu-date --> +afu.zip.ver.0=<!-- annotation-tools-zip-version --> +afu.zip.ver.1=<!-- /annotation-tools-zip-version --> +afu.cfr.pattern=Annotation File Utilities v([^"]+) \ No newline at end of file
diff --git a/annotation-file-utilities/build.xml b/annotation-file-utilities/build.xml new file mode 100644 index 0000000..58b2ad4 --- /dev/null +++ b/annotation-file-utilities/build.xml
@@ -0,0 +1,575 @@ +<?xml version="1.0"?> + +<project name="annotation-file-utilities" default="zipfile"> + <description> + Ant build file for the annotation file utilities. + Run "ant -projecthelp" for a full list of options. + </description> + + <property environment="env"/> + + <property name="java-version" value="8"/> + + <tstamp> + <format property="TIME" pattern="yy-MM-dd-HH-mm-ss-SS" /> + </tstamp> + <!-- Avoid conflicts between multiple users on the same computer. --> + <property name="tmpdir" location="${java.io.tmpdir}/${user.name}/${TIME}" /> + <!-- The distribution is the result of zipping this directory. --> + <property name="temp-annotation-tools" location="${tmpdir}/annotation-tools" /> + + <!-- Can't I just reuse temp-annotation-file-utilities, rather than + having this separate directory? --> + <property name="temp-jarfile" location="${tmpdir}/annotation-file-utilities-jarfile" /> + + <target name="init-properties"> + <condition property="exists.build.properties"> + <available file="build.properties"/> + </condition> + <fail + unless="exists.build.properties" + message="Local build.properites file is missing."/> + + <property file="build.properties"/> + + <fail + unless="global.build.properties" + message="Local build.properties file did not define global buildfile in property global.build.properties"/> + <condition property="exists.global.build.properties"> + <available file="${global.build.properties}"/> + </condition> + <fail + unless="exists.global.build.properties" + message="File ${global.build.properties} file not found."/> + <property file="${global.build.properties}"/> + + <fail + unless="user.build.properties" + message="Local build.properties file did not define global buildfile in property user.build.properties"/> + <condition property="exists.user.build.properties"> + <available file="${user.build.properties}"/> + </condition> + <fail + unless="exists.user.build.properties" + message="File ${user.build.properties} file not found."/> + <property file="${user.build.properties}"/> + + <echo message="annotations-compiler: ${annotations-compiler}"/> + + <!-- convert relative to absolute pathname --> + <property name="annotations-compiler-absolute" location="${annotations-compiler}" /> + + </target> + + <!-- Compiles all the subparts of the Annotation File Utilities. --> + <target name="init-dependencies" + depends="init-properties"> + <ant dir="${asmx}" target="bin"> + <property name="product.noshrink" value="true"/> + </ant> + <ant dir="${scene-lib}" target="bin"/> + <!-- repository version only: --> + <!-- I don't see the need for this. --> + <!-- <ant dir="${annotations-compiler}" antfile="make/build.xml" target="build"/> --> + </target> + + <target name="init" + depends="init-properties, init-dependencies"> + <fileset dir="src" id="src-files"> + <include name="**/*.java"/> + </fileset> + + <path id="libpath"> + <!-- alternate for distribution: + <pathelement location="${annotations-compiler}/bin"/> + --> + <pathelement location="${annotations-compiler}/dist/lib/javac.jar"/> + <pathelement location="${annotation-tools}/annotation-file-utilities/lib/plume-core.jar"/> + <pathelement location="${annotation-tools}/annotation-file-utilities/lib/guava-20.0.jar"/> + <pathelement location="${scene-lib}/bin"/> + <pathelement location="${asmx}/bin"/> + <!-- needed for optionsdoc target --> + <pathelement location="bin"/> + <!-- additional for distribution: + <pathelement location="${jre1.6.0}"/> + --> + </path> + </target> + + <path id="javadoc-sourcepath"> + <pathelement location="src"/> + </path> + + <target name="javadoc-clean"> + <delete dir="javadoc"/> + </target> + + <target name="javadoc" depends="javadoc-clean, init"> + <javadoc sourcepathref="javadoc-sourcepath" + classpathref="libpath" + failonerror="true" + packagenames="*" + destdir="javadoc" + access="public" + /> +<!-- + noqualifier="annotations:annotations.el:annotations.field:annotations.io:annotations.io.classfile:annotations.util:annotations.util.coll:java.lang" +--> + </target> + + <target name="optionsdoc" depends="javadoc, init"> + <javadoc sourcepathref="javadoc-sourcepath" + sourcefiles="src/annotator/Main.java" + classpathref="libpath" + docletpathref="libpath" + failonerror="true" + > + <doclet name="plume.OptionsDoclet"> + <param name="-format" value="javadoc"/> + <param name="-i"/> + <param name="-docfile" value="src/annotator/Main.java"/> + </doclet> + </javadoc> + + <javadoc sourcepathref="javadoc-sourcepath" + sourcefiles="src/annotator/Main.java" + classpathref="libpath" + docletpathref="libpath" + failonerror="true" + > + <doclet name="plume.OptionsDoclet"> + <param name="-format" value="html"/> + <param name="-i"/> + <param name="-docfile" value="annotation-file-utilities.html"/> + </doclet> + </javadoc> + + </target> + + <target name="jarfile.check.uptodate"> + <uptodate property="bin.uptodate" targetfile="annotation-file-utilities.jar"> + <srcfiles dir="bin" /> + </uptodate> + <uptodate property="asmx.bin.uptodate" targetfile="annotation-file-utilities.jar"> + <srcfiles dir="${asmx}/bin" excludes="tmp/**"/> + </uptodate> + <uptodate property="scene-lib.bin.uptodate" targetfile="annotation-file-utilities.jar"> + <srcfiles dir="${scene-lib}/bin" excludes="annotations-expected/**,annotations/tests/**"/> + </uptodate> + <uptodate property="annotations-compiler.uptodate" targetfile="annotation-file-utilities.jar" srcfile="${annotations-compiler}/dist/lib/javac.jar"/> + <uptodate property="plume.uptodate" targetfile="annotation-file-utilities.jar" srcfile="lib/plume-core.jar"/> + <uptodate property="google-collect.uptodate" targetfile="annotation-file-utilities.jar" srcfile="lib/guava-20.0.jar"/> + + <condition property="jarfile.uptodate"> + <and> + <isset property="bin.uptodate"/> + <isset property="asmx.bin.uptodate"/> + <isset property="scene-lib.bin.uptodate"/> + <isset property="annotations-compiler.uptodate"/> + <isset property="plume.uptodate"/> + <isset property="google-collect.uptodate"/> + </and> + </condition> + + <echo message="bin.uptodate: ${bin.uptodate}"/> + <echo message="asmx.bin.uptodate: ${asmx.bin.uptodate}"/> + <echo message="scene-lib.bin.uptodate: ${scene-lib.bin.uptodate}"/> + <echo message="annotations-compiler.uptodate: ${annotations-compiler.uptodate}"/> + <echo message="plume.uptodate: ${plume.uptodate}"/> + <echo message="google-collect.uptodate: ${google-collect.uptodate}"/> + <echo message="jarfile.uptodate: ${jarfile.uptodate}"/> + + </target> + + <target name="jarfile" + depends="init,build,jarfile.check.uptodate" + unless="jarfile.uptodate" + description="create the class library annotation-file-utilities.jar"> + + <echo message="Using temporary directory: ${temp-jarfile}"/> + <delete dir="${temp-jarfile}"/> + <mkdir dir="${temp-jarfile}"/> + + <echo message="Copying .class files to ${temp-jarfile}"/> + <copy todir="${temp-jarfile}"> + <fileset dir="bin" /> + <fileset dir="${asmx}/bin" excludes="tmp/**"/> + <fileset dir="${scene-lib}/bin" excludes="annotations-expected/**,annotations/tests/**"/> + </copy> + <!-- Also need to get class files in libraries --> + <unjar src="lib/plume-core.jar" dest="${temp-jarfile}"> + <patternset> + <include name="**/*.class"/> + <exclude name="META-INF/" /> + </patternset> + </unjar> + <unjar src="lib/guava-20.0.jar" dest="${temp-jarfile}"> + <patternset> + <include name="com/google/common/base/**/*.class"/> + <include name="com/google/common/collect/**/*.class"/> + <include name="com/google/common/escape/**/*.class"/> + <exclude name="META-INF/" /> + </patternset> + </unjar> + <unjar src="${annotations-compiler}/dist/lib/javac.jar" dest="${temp-jarfile}"> + <patternset> + <include name="**/*.class"/> + <exclude name="META-INF/" /> + </patternset> + </unjar> + + <!-- Actually create a single .jar file of all the class files, + scripts and documentation --> + <echo message="Creating jarfile annotation-file-utilities.jar"/> + <jar destfile="annotation-file-utilities.jar"> + <fileset dir="${temp-jarfile}"/> + </jar> + + <!-- Delete class files copied over --> + <echo message="Deleting temporary directory: ${temp-jarfile}"/> + <delete dir="${temp-jarfile}"/> + </target> + + <target name="check-git-status" depends="init-properties"> + <exec executable="git" failonerror="true" + outputproperty="status.output"> + <arg value="status" /> + </exec> + + <condition property="status.output.empty"> + <equals + arg1="${status.output}" + arg2=""/> + </condition> + + <fail unless="status.output.empty" + message="`git status' did not return empty output. + Commit/add/remove files as appropriate, then re-try."/> + </target> + + <!-- Copy files from repository to temporary directory from which they + will be packaged up. --> + <target name="update-workspace" depends="init-properties"> + <delete dir="${temp-annotation-tools}" /> + + <exec executable="git" failonerror="true"> + <arg value="clone" /> + <arg value="${annotation-tools}" /> + <arg value="${temp-annotation-tools}" /> + </exec> + <delete dir="${temp-annotation-tools}/scene-lib.orig-hand-annos" /> + <delete dir="${temp-annotation-tools}/.git" /> + <delete file="${temp-annotation-tools}/.gitignore" /> + <delete file="${temp-annotation-tools}/.hg_archival.txt" /> + <delete file="${temp-annotation-tools}/.hgignore" /> + + <copy todir="${temp-annotation-tools}/annotation-file-utilities"> + <fileset dir="${afu}"> + <include name="annotation-file-format.dvi"/> + <include name="annotation-file-format.html"/> + <include name="annotation-file-format.pdf"/> + <include name="annotation-file-utilities.jar"/> + <include name="bin/**"/> + </fileset> + </copy> + + <copy todir="${temp-annotation-tools}/asmx"> + <fileset dir="${asmx}"> + <include name="bin/**"/> + </fileset> + </copy> + + <copy todir="${temp-annotation-tools}/scene-lib"> + <fileset dir="${scene-lib}"> + <include name="bin/**"/> + </fileset> + </copy> + + </target> + + <target name="zipfile" + depends="jarfile,annotation-file-format,run-tests,update-workspace" + description="create the distribution: annotation-tools.zip"> + <!-- Create a new directory containing all the files and then zip that + directory, so that when the user unzips they extract exactly one + directory. --> + + <!-- In order for the shell scripts to have the proper execution bit set, + include them specifically with the right permissions. Ant presently + does not use the file's permissions themselves to do this. --> + <zip destfile="annotation-tools.zip" compress="true"> + <fileset dir="${tmpdir}"> + <include name="annotation-tools/"/> + <exclude name="annotation-tools/annotation-file-utilities/scripts/extract-annotations"/> + <exclude name="annotation-tools/annotation-file-utilities/scripts/insert-annotations"/> + <exclude name="annotation-tools/annotation-file-utilities/scripts/insert-annotations-to-source"/> + </fileset> + <zipfileset dir="${tmpdir}" filemode="755"> + <include name="annotation-tools/annotation-file-utilities/scripts/extract-annotations"/> + <include name="annotation-tools/annotation-file-utilities/scripts/insert-annotations"/> + <include name="annotation-tools/annotation-file-utilities/scripts/insert-annotations-to-source"/> + </zipfileset> + </zip> + + <!-- Delete temporary files once they have been zipped. --> +<!-- + <delete dir="${temp-annotation-file-utilities}"/> +--> + </target> + + <macrodef name="update"> + <attribute name="file"/> + <attribute name="start"/> + <attribute name="end" default=""/> + <attribute name="with"/> + <sequential> + <echo level="info" message="updating @{file}"/> + <replaceregexp file="@{file}" byline="true" + match="@{start}.*@{end}" replace="@{start}@{with}@{end}"/> + </sequential> + </macrodef> + + <target name="update-versions" depends="init-properties"> + <fail unless="release.ver" message="You must specify a release version to update to"/> + <fail unless="release.date" message="You must specify a release date to update to"/> + + <property name="release.version.regexp" value="\d\.\d\.\d+(?:\.\d)"/> + <property name="afuWebPage" value="${annotation-tools}/annotation-file-utilities/annotation-file-utilities.html"/> + + <replaceregexp file="${afuWebPage}" byline="true" + match="annotation-tools-${release.version.regexp}{0,1}.zip" replace="annotation-tools-${release.ver}.zip"/> + + <update file="${afuWebPage}" + start="${afu.zip.ver.0}" end="${afu.zip.ver.1}" + with="annotation-tools-${release.ver}.zip"/> + + <update file="${afuWebPage}" + start="${afu.ver.0}" end="${afu.ver.1}" + with="${release.ver}, ${release.date}"/> + + <update file="${afuWebPage}" + start="${afu.date.0}" end="${afu.date.1}" + with="${release.date}"/> + + <property name="newCfrValue" value="Annotation File Utilities v${release.ver}"/> + + <property name="ClassFileReaderPath" value="${annotation-tools}/scene-lib/src/annotations/io/classfile/ClassFileReader.java"/> + <echo level="info" message="updating ${ClassFileReaderPath}"/> + <replaceregexp file="${ClassFileReaderPath}" byline="true" + match="${afu.cfr.pattern}" replace="${newCfrValue}"/> + </target> + + <!-- TODO: I am not sure this target works as the original author intended + TODO: (i.e. I don't think check-git-status gets executed twice) + TODO: but I am maintaining the previous behavior. + --> + <target name="web" depends="check-git-status,web-no-checks,check-git-status"/> + + <!-- New release process runs checks prior to running the web-no-checks target--> + <target name="web-no-checks" depends="clean,zipfile" + description="export the zipfile, etc. to its website"> + + <fail unless="deploy-dir" message="You must specify a deploy-dir, the live site deploy-dir=/cse/www2/types/annotation-file-utilities/releases/_version num_"/> + + <fail unless="afu.version" message="You must specify an afu.version, such as 3.6.22"/> + + <echo message="Export location: ${deploy-dir}"/> + <echo message="Copying annotation-tools.zip"/> + <copy file="annotation-tools.zip" + tofile="${deploy-dir}/annotation-tools-${afu.version}.zip"/> + + <echo message="Copying annotation-file-utilities.html"/> + <copy file="annotation-file-utilities.html" + todir="${deploy-dir}"/> + <copy file="changelog.html" + todir="${deploy-dir}"/> + + <echo message="Copying annotation-file-format.{html,pdf}"/> + <copy file="annotation-file-format.html" + todir="${deploy-dir}"/> + <copy file="annotation-file-format.pdf" + todir="${deploy-dir}"/> + + <copy todir="${deploy-dir}/figures" flatten="true"> + <fileset dir="figures"> + <include name="*.svg"/> + <include name="*.png"/> + <include name="*.gif"/> + </fileset> + </copy> + + <symlink overwrite="true" + link="${deploy-dir}/index.html" + resource="annotation-file-utilities.html"/> + </target> + + <target name="annotation-file-format-clean" + description="removes generated documentation files"> + <delete file="annotation-file-format.aux"/> + <delete file="annotation-file-format.dvi"/> + <delete file="annotation-file-format.haux"/> + <delete file="annotation-file-format.html"/> + <delete file="annotation-file-format.htoc"/> + <delete file="annotation-file-format.log"/> + <delete file="annotation-file-format.pdf"/> + <delete file="annotation-file-format.toc"/> + <exec executable="make" failonerror="true"> + <arg value="-C"/> + <arg value="figures"/> + <arg value="clean"/> + </exec> + <delete file="scene-lib-type-hierarchy.png"/> + </target> + + <!-- + A problem is that the document date is the date that LaTeX was run + rather than the date of last modification; that should be fixed in + the document, perhaps. --> + <target name="annotation-file-format" + description="Make documentation: annotation-file-format.{html,pdf}"> + <exec executable="make" failonerror="true"> + <arg value="-C"/> + <arg value="figures"/> + </exec> + <exec executable="latex" failonerror="true"> + <arg value="annotation-file-format.tex"/> + </exec> + <exec executable="bibtex" failonerror="true"> + <arg value="annotation-file-format"/> + </exec> + <exec executable="latex" failonerror="true"> + <arg value="annotation-file-format.tex"/> + </exec> + <exec executable="latex" failonerror="true"> + <arg value="annotation-file-format.tex"/> + </exec> + <exec executable="pdflatex" failonerror="true"> + <arg value="annotation-file-format.tex"/> + </exec> + <exec executable="hevea" failonerror="true"> + <arg value="-fix"/> + <arg value="-exec"/> + <arg value="xxdate.exe"/> + <arg value="urlhref.hva"/> + <arg value="annotation-file-format.tex"/> + </exec> + </target> + + <target name="clean" depends="annotation-file-format-clean,clean-tests" + description="removes generated files (e.g., .jar, .zip)"> + <delete dir="bin"/> + + <!-- <echo message="Deleting temporary directory: ${temp-jarfile}"/> --> + <delete dir="${temp-jarfile}"/> + <delete dir="${temp-annotation-file-utilities}"/> + + <!-- <echo message="Deleting previous distribution: annotation-file-utilities.{jar,zip}"/> --> + <delete file="annotation-file-utilities.jar"/> + <delete file="annotation-tools.zip"/> + </target> + + <target name="run-tests" depends="init-properties" + description="run tests for the annotator"> + <exec dir="tests/" executable="make" failonerror="true"> + <env key="XJAVAC" value="${annotations-compiler-absolute}/dist/bin/javac -g ${xjavac.args}"/> + <env key="JAVAC_JAR" value="${annotations-compiler-absolute}/dist/lib/javac.jar"/> + </exec> + </target> + + <target name="clean-tests" description="removes generated test files"> + <exec dir="tests/" executable="make" failonerror="true"> + <arg value="clean"/> + </exec> + <exec dir="tests/source-extension/" executable="make" failonerror="true"> + <arg value="clean"/> + </exec> + <exec dir="tests/system-test/" executable="make" failonerror="true"> + <arg value="clean"/> + </exec> + </target> + + <target name="bin" + depends="build"/> + + <target name="build" + depends="init" + description="compile all source files"> + + <mkdir dir="bin"/> + + <fileset dir="src" id="javacSrc"> + <include name="**/*.java" /> + </fileset> + <pathconvert property="cmdTxts" refid="javacSrc" pathsep=" " /> + + <java fork="true" + failonerror="true" + classpathref="libpath" + classname="com.sun.tools.javac.Main"> + <arg value="-Xlint:-options"/> + <arg value="-Werror"/> + <arg value="-g"/> + <arg line="-sourcepath src"/> + <arg line="-d bin"/> + <arg line="${cmdTxts}"/> + <arg line="-version"/> + <!-- To prevent a cyclic dependency with the Checker + Framework, ignore type annotations in comments here. + Separate targets exist to check the qualifiers. --> + <arg line="-XDTA:noannotationsincomments"/> + <!-- Make sure we only have Java 7 source code and generate Java 7 bytecode. --> + <arg value="-source"/> + <arg value="7"/> + <arg value="-target"/> + <arg value="7"/> + </java> + + </target> + + <!-- Boilerplate to set jsr308javac property. Is there a better way? --> + <property environment="env"/> + <condition property="isUnix"> + <os family="unix" /> + </condition> + <condition property="isWindows"> + <os family="windows" /> + </condition> + <target name="init-jsr308javac-unix" if="isUnix"> + <property name="jsr308javac" value="${env.CHECKERFRAMEWORK}/checker/bin/javac" /> + </target> + <target name="init-jsr308javac-windows" if="isWindows"> + <property name="jsr308javac" value="${env.CHECKERFRAMEWORK}/checker/bin/javac.bat" /> + </target> + + <target name="check-nullness" + description="Check for nullness errors." + depends="init,clean,init-jsr308javac-unix,init-jsr308javac-windows"> + <mkdir dir="bin"/> + <javac srcdir="src" destdir="bin" debug="true" encoding="8859_1" + fork="yes" + executable="${jsr308javac}"> + <compilerarg value="-version"/> + <compilerarg line="-target 5"/> + <compilerarg line="-processor org.checkerframework.checker.nonnull.NonNullFbcChecker"/> + <compilerarg value="-implicit:class"/> + <compilerarg line="-Awarns -Xmaxwarns 10000"/> + <classpath> + <path refid="libpath"/> + <pathelement location="${env.annotations}/checker/dist/checker.jar"/> + </classpath> + </javac> + </target> + + + <!-- This tags table includes the scene library. --> + <target name="etags" depends="tags"> + </target> + <target name="tags" + description="create Emacs TAGS table, including scene-lib files"> + <exec executable="/bin/sh" failonerror="true"> + <arg value="-c"/> + <arg value="etags `find src -name '*.java' | sort-directory-order` `find ../scene-lib -name '*.java' | sort-directory-order`"/> + </exec> + </target> + +</project>
diff --git a/annotation-file-utilities/changelog.html b/annotation-file-utilities/changelog.html new file mode 100644 index 0000000..081f896 --- /dev/null +++ b/annotation-file-utilities/changelog.html
@@ -0,0 +1,852 @@ +<!DOCTYPE html> +<html> +<head> + <title>Annotation File Utilities Changelog</title> +</head> +<body> +<h1>Annotation File Utilities Changelog</h1> + +<p> +This is the changelog for the +<a href="./">Annotation File Utilities</a>. +</p> + +<ul> + <li> + Version 3.6.45 (released September 5, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.44 (released August 3, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.43 (released July 3, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.42 (released June 1, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.41 (released May 1, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.40 (released April 3, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.39 (released March 1, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.38 (released Jan 20, 2017): + <ul> + <li> The Annotation File Utilities webpage has moved to + <a href="https://checkerframework.org/annotation-file-utilities/">https://checkerframework.org/annotation-file-utilities/</a>. + Old URLs should redirect to the new one, but please update your links + and let us know if any old links are broken rather than redirecting.</li> + </ul> + </li> + <li> + Version 3.6.37 (released Jan 3, 2017): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.36 (released Dec 1, 2016): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.35 (released Nov 2, 2016): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.34 (released Oct 3, 2016): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.33 (released Sep 16, 2016): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.32 (released Sep 1, 2016): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.31 (released August 1, 2016): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.30 (released July 1, 2016): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.29 (released Jun 1, 2016): + <ul> + <li> + extract-annotations no longer puts package annotations on non-existent class, + fixing Issue <a href="https://github.com/typetools/annotation-tools/issues/117">#117</a>. + </li> + <li> + Closes issue <a href="https://github.com/typetools/annotation-tools/issues/120">#120</a>. + </li> + </ul> + Version 3.6.28 (released May 1, 2016): + <ul> + <li> + <code>AScene</code> now has a copy constructor and implements + <code>Cloneable</code>. + </li> + </ul> + </li> + <li> + Version 3.6.27 (released Apr 1, 2016): + <ul> + <li> + The annotation file format permits use of a class name, in addition to + <code><init></code>, to specify a constructor. + </li> + <li> + Closes issues + <a href="https://github.com/typetools/annotation-tools/issues/70">#70</a> + and + <a href="https://github.com/typetools/annotation-tools/issues/88">#88</a>. + </li> + </ul> + </li> + <li> + Version 3.6.26 (released Mar 1, 2016): + <ul> + <li>Avoids reinserting an annotation that already exists in comments, fixing Issue + <a href="https://github.com/typetools/annotation-tools/issues/114">#114</a>.</li> + </ul> + </li> + <li> + Version 3.6.25 (released Feb 1, 2016): + <ul> + <li>Uses system javac to compile the executable program, thus enabling Java 7 execution.</li> + <li>Clears set of required imports after processing each source file, fixing Issue + <a href="https://github.com/typetools/annotation-tools/issues/111">#111</a>.</li> + <li>Escapes String fields of annotations, fixing Issue + <a href="https://github.com/typetools/annotation-tools/issues/112">#112</a>.</li> + </ul> + </li> + <li> + Version 3.6.24 (released Jan 4, 2016): + <ul> + <li>Fixes Issue + <a href="https://github.com/typetools/annotation-tools/issues/108">#108</a> + by adding a <code>LICENSE.txt</code> file.</li> + <li>Fixes offset calculation for wide JVM instructions.</li> + <li>Makes minor improvements to build process.</li> + </ul> + </li> + <li> + Version 3.6.23 (released Dec 1, 2015): + <ul> + <li>Fixes Issue + <a href="https://github.com/typetools/annotation-tools/issues/106">#106</a>.</li> + <li>Makes minor improvements to documentation and build process.</li> + </ul> + </li> + <li> + Version 3.6.22 (released Nov 9, 2015): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.21 (released Oct 24, 2015): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.20 (released Oct 8, 2015): + <ul> + <li>Minor bug fixes and improvements.</li> + </ul> + </li> + <li> + Version 3.6.19 (released Sep 1, 2015): + <ul> + <li>Avoids importing the same annotation name from multiple packages (Issue + <a href="https://github.com/typetools/annotation-tools/issues/101">#101</a>).</li> + <li>Fixes some bugs (including Issues + <a href="https://github.com/typetools/annotation-tools/issues/100">#100</a> and + <a href="https://github.com/typetools/annotation-tools/issues/102">#102</a>) + pertaining to <code>extends</code> clause generation.</li> + </ul> + </li> + <li> + Version 3.6.18 (released Aug 4, 2015): + <ul> + <li> + Moved the Annotation Tools version control repository from Google Code + to GitHub, and from the Mercurial version control system to Git. + If you have cloned the version control repository, then discard your + old clone and create a new one using this command: + <pre> git clone https://github.com/typetools/annotation-tools.git</pre> + </li> + <li>Fixes some errors involving default class and method bound insertion.</li> + </ul> + </li> + <li> + Version 3.6.17 (released July 1, 2015): + <ul> + <li> + Minor bug fixes and improvements. + </li> + </ul> + </li> + <li> + Version 3.6.16 (released June 1, 2015): + <ul> + <li> + Fixes multiple crash-causing bugs in <code>insert-annotations-to-source</code>. + </li> + </ul> + </li> + <li> + Version 3.6.15 (released May 1, 2015): + <ul> + <li> + Allows annotations on constituents of an intersection type cast without the use of the AST path format. + </li> + <li> + Fixes assorted bugs in <code>insert-annotations</code>. + </li> + </ul> + </li> + <li> + Version 3.6.14 (released April 17, 2015): + <ul> + <li> + Fixes numerous minor bugs in <code>insert-annotations</code> and <code>insert-annotations-to-source</code>. + </li> + </ul> + </li> + <li> + Version 3.6.13 (released March 2, 2015): + <ul> + <li> + Extends support for lambda expressions, member references, and type arguments of static method calls and member references to <code>extract-annotations</code> and <code>insert-annotations</code>. + </li> + <li> + Slightly changes JAIF format for the new features. (See file format documentation for details.) + </li> + </ul> + </li> + <li> + Version 3.6.12 (released January 30, 2015): + <ul> + <li> + Adds support for lambda expressions, member references, and type arguments of static method calls and member references to <code>insert-annotations-to-source</code>. + </li> + </ul> + </li> + <li> + Version 3.6.11 (released December 19, 2014): + <ul> + <li> + Fixes more insertion logic bugs, including errors in handling generic arrays. + </li> + </ul> + </li> + <li> + Version 3.6.10 (released November 26, 2014): + <ul> + <li> + Fixes many bugs, mostly in AST path-based insertion logic. + </li> + </ul> + </li> + <li> + Version 3.6.9 (released October 30, 2014): + <ul> + <li> + Minor bug fixes and improvements. + </li> + </ul> + </li> + <li> + Version 3.6.8 (released September 24, 2014): + <ul> + <li> + Fixes numerous bugs in AST path-based insertion logic. + </li> + </ul> + </li> + <li> + Version 3.6.7 (released August 1, 2014): + <ul> + <li> + Improves Java 9 support. + </li> + <li> + Fills in some previously missing cases for AST path-based insertion specifications. + </li> + </ul> + </li> + <li> + Version 3.6.6 (released July 1, 2014): + <ul> + <li> + Fixes bug affecting inner array types of new[] expressions in the AST path-based insertion logic. + </li> + <li> + Fixes JAIF generation bugs involving insert-annotation and insert typecast within methods and fields. + </li> + </ul> + </li> + <li> + Version 3.6.5 (released June 2, 2014): + <ul> + <li> + Generates extends bounds when necessary for annotation insertion. + </li> + <li> + Fixes assorted bugs in the AST path-based insertion logic. + </li> + </ul> + </li> + <li> + Version 3.6.4 (released May 1, 2014): + <ul> + <li> + Expands AST path support to cover every AST node within a class declaration. + </li> + <li> + Abbreviates enum tags for annotations if <code>--abbreviate</code> is selected. + </li> + <li> + Generates explicit default constructors when necessary for annotation insertion. + </li> + </ul> + </li> + <li> + Version 3.6.3 (released Apr 2, 2014): + <ul> + <li> + Eliminates insert-annotation problems with handling Java 8 class files. + </li> + <li> + Corrects annotation placement within a qualified type name. + </li> + <li> + Fills in gaps in AST Path API. All (Java 7) non-declaration nodes can now be identified by a path from an enclosing declaration. + </li> + <li> + Expands scene-lib API with visitor framework and new class <code>APIIndex</code>, which provides a cache that maps Java abstract syntax tree nodes to AST paths as defined in the annotation file format specification. + </li> + </ul> + </li> + <li> + Version 3.6.2 (released Mar 5, 2014): + <ul> + <li> + Expands scene-lib API with visitor framework and new class <code>APIIndex</code>, which provides a cache that maps Java abstract syntax tree nodes to AST paths as defined in the annotation file format specification. + </li> + </ul> + </li> + <li> + Version 3.6.1 (released Feb 19, 2014): + <ul> + <li> + Adds missing cases for AST path-based annotation/cast insertion. + </li> + </ul> + </li> + <li> + Version 3.5.9 (released Jan 2, 2014): + <ul> + <li> + Fixes assorted minor bugs. + </li> + </ul> + </li> + <li> + Version 3.5.8 (released Dec 9, 2013): + <ul> + <li> + Creates output directory in current working directory (by default), rather than relative to input pathname. + </li> + <li> + Recognizes "instanceinit" keyword in JAIF for adding annotations inside instance initializers. + </li> + <li> + Corrects syntax of results of inserting annotations on bare array literals. + </li> + <li> + More fully treats enum, interface, and annotation declarations in accordance with the rules for class declarations. + </li> + </ul> + </li> + <li> + Version 3.5.7 (released Nov 15, 2013): + <ul> + <li> + Handles type paths with INNER_TYPE. + </li> + <li> + Disallows annotations on generic array type bounds and on wildcard bounds in "instanceof" expressions, since Java 8 prohibits type bounds in those locations. + </li> + <li> + Vastly improves performance when index file covers numerous source files, by filtering out irrelevant insertions at an earlier stage. + </li> + <li> + Avoids stack overflow during regex matching. + </li> + </ul> + </li> + <li> + Version 3.5.6 (released Oct 23, 2013): + <ul> + <li> + Adds "insert-annotation" to JAIF format, allowing the use of AST paths to specify source locations for annotation insertion (which previously was possible only for typecast insertions). + </li> + <li> + For consistency with <code>extract-annotations</code>, <code>insert-annotations-to-source</code> now matches generic method types in the source with JAIF specifications for their least upper bounds. + </li> + <li> + Allows annotations on inner type declarations of parameterized types. + </li> + <li> + Avoids spurious code location matches on anonymous inner subclasses of an outer class. + </li> + <li> + Works around Java regex bug that on some input causes an infinite loop in the Matcher. + </li> + <li> + Fixes many other bugs, most involving receiver insertions. + </li> + <li> + No longer aborts on (non-fatal) warnings found during the <code>javac</code> type-checking phase of <code>insert-annotations-to-source</code>. + </li> + </ul> + </li> + <li> + Version 3.5.5 (released Aug 28, 2013): + <ul> + <li> + Allows annotation insertion at constructor "return type" as per the specification. + </li> + <li> + Fixes assorted bugs dealing with type qualification, inner classes, and receiver insertion. + </li> + </ul> + </li> + <li> + Version 3.5.4 (released Aug 1, 2013): + <ul> + <li> + Allow the use of command-line argument files. Argument file rules are identical to those of javac. + </li> + <li> + The Annotation File Utilities now appropriately handles vararg parameters. + </li> + <li> + Minor documentation improvements. + </li> + </ul> + </li> + <li> + Version 3.5.3 (released May 1, 2013): + <ul> + <li> + Don't depend on bootstrap javac files. + </li> + <li> + Other minor improvements. + </li> + </ul> + </li> + <li> + Version 3.5.2 (released April 4, 2013): + <ul> + <li> + Allow annotations on compound types of a cast insertion. + </li> + <li> + Added <code>--print_error_stack</code> command-line option to + <code>insert-annotations-to-source</code> tool to print a stack trace on + an error. + </li> + <li> + Various bug fixes. + </li> + <li> + Improvements to the Annotation File Format manual. + <ul> + <li> + Clarified "Grammar" section by reordering and adding more details. + </li> + <li> + Added "Design and Implementation Details" section. + </li> + </ul> + </li> + </ul> + </li> + <li> + Version 3.5.1 (released March 1, 2013): + <ul> + <li> + Small improvements to error messages when parsing JAIF files and for + illegal source code insertions. + </li> + <li> + Installation instruction and manual improvements. + </li> + <li> + Better handling of source code insertions in anonymous and local + classes. + <ul> + <li> + Allow source code insertions in a local class (a class defined + within a method). + </li> + <li> + Bug fixes for anonymous and local class indexes. + </li> + <li> + Don't traverse into nested classes when calculating a source code + index. + </li> + </ul> + </li> + <li> + Improved support for source code method declaration receiver parameter + insertions. + <ul> + <li> + Use the full type name (Outer.Inner1.Inner2) for receiver insertions + in inner classes. + </li> + <li> + Allow annotation insertions on compound types of method declaration + receiver parameters. + </li> + </ul> + </li> + <li> + Insert source code annotations in the semantically correct place for + qualified types. + </li> + <li> + For bytecode annotation extraction and insertion read and put "local" + type annotations in the Code attribute, not the Method attribute. + </li> + </ul> + </li> + + <li> + Version 3.5 (released February 1, 2013): + <ul> + <li> + Updated to JSR308 bytecode representation and receiver parameter syntax. + </li> + <li> + Support source code insertion of annotated casts. + </li> + <li> + Support adding annotations to bytecode for type casts with intersection + types. + </li> + </ul> + </li> + + <li> + Version 3.4 (released September 11, 2012): + <ul> + <li> + Adapted to underlying changes in JSR 308. + </li> + <li> + Support .class literals as annotation attributes. + </li> + </ul> + </li> + + <li> + Version 3.3 (released September 20, 2011): + <ul> + <li> + Improved support for annotations on array creation expressions. + </li> + <li> + Small tweaks to the code and documentation. + </li> + </ul> + </li> + + <li> + Version 3.2 (released June 18, 2011): + <ul> + <li> + Support source code indexes, in contrast to the bytecode + centric indexes supported so far, which makes it easier for + source code centric tools to emit AFU files. + See Section "Support for source code indexes" in the + <a href="annotation-file-format.html">manual</a>. + </li> + <li> + Support annotating the bounds of class and method type + parameters and of wildcards, in particular also support + implicit bounds and make them explicit. + Also support adding declaration annotations on type parameters. + </li> + <li> + Better support for anonymous inner classes. + </li> + <li> + Improve handling of already existing annotations, in + particular for generic types. + </li> + <li> + Consistently do matching on methods using the erased signature. + </li> + <li> + Specify annotations on extends and implements clauses. + </li> + <li> + Handle interfaces, enums, etc. like classes. + </li> + <li> + Numerous small bug fixes and improvements. + </li> + </ul> + </li> + + <li> + Version 3.1 (released September 18, 2010): + <ul> + <li> + Don't duplicate annotations that are in the source already. + </li> + <li> + Add <code>--omit-annotation</code> option to omit a given annotation + (example: <code>@ThisMutable</code>, which is the default and need not be + inserted in source files). + </li> + <li> + Bug fixes related to inner classes, array levels, multiple methods + with same signature but different return type (impossible in Java + code, but can occur in class files), parameter indices. + </li> + <li> + Switch from utilMDE library to + <a href="https://github.com/mernst/plume-lib">plume-lib</a>. + </li> + </ul> + </li> + + <li> + Version 3.0 (released December 21, 2009): + <ul> + <li> + Handle meta-annotations. + Previously, the Annotation File Utilities didn't handle meta-annotations + (annotations that are written on an annotation type declaration); in fact, + AFU simply fabricated retention information. Now, meta-annotations are + handled uniformly with other annotations. + </li> + + <li> + Distinguish type annotations from declaration annotations. + Previously, AFU assumed that all annotations were type annotations. Now, + this information is looked up from the meta-annotations and is + distinguished in the file format (see below). + </li> + + <li> + Read annotations from classfiles. + Previously, AFU created partial information for annotations in an ad hoc + way, and classfile reading did not work. (The focus was on inserting + annotations in class files, not reading annotations from class files.) + Now, classfile reading is operational, and it is often not necessary to + declare annotations in an annotation file. However, if the annotation is + declared in the annotation file, it should be consistent with its + declaration in the class file. + </li> + + <li>Changes to annotation file format. + + <ul> + <li>Indicating meta-annotations + + <pre> annotation visible @Nullable:</pre> + + becomes + + <pre> annotation @Nullable: @Retention(value=RUNTIME)</pre> + + and if it's a type annotation, it should be + + <pre> annotation @Nullable: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})</pre> + </li> + <li>Locations for type and declaration annotations. + + If p2.B is a declaration annotation on a method, it is written as before: + + <pre> method foo(Ljava/util/Set;)V: @p2.B</pre> + + If p2.B is a type annotation on a method return type, it is written in a + new location: + + <pre> method foo(Ljava/util/Set;)V: + return: @p2.B</pre> + + Fields and method parameters are treated similarly, but with + <code>type</code> marker instead of <code>return</code>. So if p2.A is a + declaration annotation targeting the field, while p2.B is a type + annotation on the field, the field would be declared as: + + <pre> field s: @p2.A + type: @p2.B</pre> + + Furthermore, <code>inner-type:</code> is permitted only after a keyword + that introduces a type, such as <code>type</code>, <code>return</code>, and + <code>receiver</code>, to prevent confusion (though the file format + becomes slightly more verbose). + + </li> + <li>Permit qualified names in several places that only identifiers were + permitted before. This enables use of names that contain periods, such + as inner classes, and also fully-qualified names. + + </li> + <li>Permit two shorthands in annotation uses, as in Java. If there is a + single field named "value", then its name may be omitted: @A(1) instead + of @A(value=1). If an array has only one element, the braces may be + omitted: @B(value=1) instead of @B(value={1}). Both + shorthands may be combined: @B(1) instead of @B(value={1}). + </li> + </ul> + </li> + </ul> + </li> + + <li> + Version 2.3.3 (released August 18, 2009): + <ul> + <li>Fix a few bugs.</li> + </ul> + </li> + + <li> + Version 2.3.2 (released August 14, 2009): + <ul> + <li>Relax file format: receiver may precede parameters (in spec & + implementation), parameters may omit "#" (in implementation only)</li> + </ul> + </li> + + <li> + Version 2.3.1 (released August 12, 2009): + <ul> + <li>Fix many bugs.</li> + </ul> + </li> + + <li> + Version 2.3 (released July 29, 2009): + <ul> + <li>Support <code>-i --in-place</code> argument.</li> + <li>Fix incorrect array brackets in tests.</li> + <li>Fix inserting annotations on receivers.</li> + <li>Improve documentation.</li> + <li>Improve error handling.</li> + </ul> + </li> + + <li> + Version 2.2 (released December 7, 2008): + <ul> + <li>Permit Main to process multiple files at a time.</li> + <li>Don't re-insert annotations that are already present.</li> + <li>Add a space before annotation if there isn't one there.</li> + <li>Fix problems with array syntax; now adheres to the current JSR + 308 syntax.</li> + <li>Improve error handling.</li> + <li>Code reorganization: remove subdirectories and classes that + added complexity without commensurate functionality.</li> + </ul> + </li> + + <li> + Version 2.1.2 (released April 28, 2008): + Minor updates to the “Annotation File Format Specification” (<a + href="https://checkerframework.org/annotation-file-utilities/annotation-file-format.pdf">PDF</a>, <a + href="https://checkerframework.org/annotation-file-utilities/annotation-file-format.html">HTML</a>). + </li> + + <li> + Version 2.1.1 (released May 31, 2007): + Added support for two new annotation target_types; consult the + classfile specification (<a + href="https://checkerframework.org/jsr308/specification/java-annotation-design.pdf">PDF</a>, + <a href="https://checkerframework.org/jsr308/specification/java-annotation-design.html">HTML</a>). + </li> + + <li> + Version 2.1 (released May 4, 2007): + Modified overall setup of distribution to be easier to understand. + </li> + + <li> + Version 2.0 (released May 1, 2007): + Initial public release. + </li> + + <li> + Version 1.0 (released March 19, 2007): + Preliminary release. + The <code>insert-to-annotations-source</code> tool only + operates on class, field and method signatures. + </li> + +</ul> + +<p> +The original developer (through version 2.1.2) was Jaime Quinonez. +</p> + + +</body> +</html> + + +<!-- LocalWords: utils bashrc tcsh tcshrc cshrc classpath AFU Ljava + -->
diff --git a/annotation-file-utilities/design.tex b/annotation-file-utilities/design.tex new file mode 100644 index 0000000..d1298da --- /dev/null +++ b/annotation-file-utilities/design.tex
@@ -0,0 +1,464 @@ +\documentclass{article} +\title{AFU v4 Design} +\author{Dan Brown} + +\usepackage{graphics} +\usepackage{verbatim} +\newenvironment{code}{\footnotesize\verbatim}{\endverbatim\normalsize} +\newenvironment{jcode}{\footnotesize\verbatim}{\endverbatim\normalsize} + +\begin{document} +\maketitle + +\section{Design Considerations} + +The Annotation File Utilities (AFU) include programs for extracting +annotations from Java class files and inserting annotations into Java +source or class files, in accordance with the Java 8 language and virtual +machine specification. This +document uses AFU in the singular, in reference to the source +insertion program except where otherwise noted. + +The AFU suffers from multiple design flaws that impede performance and +maintainability, including: + +\begin{description} +\item[Unstructured text modification.] The AFU inserts +annotations into source text instead of modifying syntax trees. +The use of source positions rather than tree paths for indicating +insertion placement has led to code that is fragile and hence difficult +to maintain. +\item[Non-uniform logic.] There are two distinct ways to +identify where to insert annotations into the source, each having its +own distinct data structures and logic. Consequently, it is possible to +have inconsistent results for different representations of the same +location. +\item[Inefficient traversal.] The search strategy is +generate-and-test: it chooses an AST node in a preorder traversal and +checks every insertion specification relevant to the class (or package) +to determine whether it applies to the current node. Furthermore, since +the implementations of some tests contain recursive calls going up the +path to the root, these tests can be repeated many times over for some +nodes. +\end{description} + +The proposed design is aimed at mitigating these problems. It makes +the traversal a single pass over the annotations, with limited +re-visitation of AST nodes. It reduces the chance for inconsistency +between formats by finding the AST node that corresponds to a code +location given in the scene before determining the insertion position. +It modifies an AST data structure rather than the source itself, +preserving formatting and commentary by incorporating non-leaf text +(i.e.\ whitespace and comments) into the representation of the AST. + +\section{Current AFU} + +The current AFU source code (3.6.16 as of 11 June 2015) is organized +into four packages (not counting the \texttt{asmx} and +\texttt{scene\_lib} sub-projects): +\begin{itemize} +\item \texttt{annotator}, containing the main class and a class +representing source text; +\item \texttt{annotator.find},\ containing the classes that implement +the core insertion logic; +\item \texttt{annotator.scanner},\ containing support classes for +\texttt{annotator.find};\ and +\item \texttt{annotator.specification},\ containing classes that +manage intermediate representations of the insertions specified in a +Java Annotation Index File (JAIF). +\end{itemize} +Should the proposed redesign be implemented, it is anticipated that the +first will be simplified somewhat and modified to reflect changes in the +other packages, the second will be radically simplified, the third will +remain the same, and the fourth will be eliminated. + +The details of \texttt{annotator.scanner}\ are not discussed here, since +the proposed redesign will neither change them nor be influenced by +them. Of the classes in \texttt{annotator.specification},\ only +\texttt{IndexFileSpecification},\ which extracts \texttt{Insertion}s +from \texttt{scene-lib}'s JAIF representation +(\texttt{annotations.el.AScene}), is relevant to the redesign. + +By far the largest and most complex package is \texttt{annotator.find}. +The insertion logic is distributed among \texttt{TreeFinder} and +numerous implementations of the \texttt{Criterion} and +\texttt{Insertion} interfaces. (A \texttt{Criterion} is a reified +condition that provides a method +\texttt{boolean isSatisfiedBy(TreePath path)}, +and an \texttt{Insertion} includes matching criteria, output parameters, +and methods for generating the source text to be inserted.) + +A simplified outline of the basic flow of the program is as follows: + +\begin{enumerate} +\item Interpret each JAIF as a ``scene'' (\texttt{AScene}), then invoke +the \texttt{parse} method in \texttt{IndexFileSpecification} to extract +the indicated \texttt{Insertion}s from the scene. +\item Parse each Java source file to obtain a (type-checked) AST. +\item Create an empty map from (Integer) source positions to +insertions. +\item For each node in the AST in pre-order: +\begin{itemize} +\item For each extracted \texttt{Insertion} that is relevant to the +immediately enclosing class (or package for compilation unit nodes): +\begin{itemize} +\item If the path from the root to the current node satisfies every +\texttt{Criterion} for the \texttt{Insertion}: +\begin{enumerate} +\item Find the appropriate source position for the annotation. +\item Add a mapping from the position to the \texttt{Insertion} to the +map. +\item Remove the insertion from the extracted set. +\end{enumerate} +\end{itemize} +\end{itemize} +\item For each position-to-insertion mapping in descending order by +position: +\begin{itemize} +\item Generate insertion text and insert at indicated position. +\end{itemize} +\item Emit modified source code. +\end{enumerate} + +\section{Proposed Revisions} + +\begin{figure}[ht] +\begin{center} +\resizebox{\columnwidth}{!}{\includegraphics{dataflow.png}} +\end{center} +\caption{ +Data flow between components. Boxes represent sources and sinks; +ellipses represent Java objects. Bold lines mark new classes and data +paths, and dashed lines indicate current data flows to be bypassed. +} +\label{fig:dataflow} +\end{figure} + +Figure~\ref{fig:dataflow} gives an overview of proposed changes to the +flow of data between classes. First, the current re-interpretation of +the scene as a ``specification'' and subsequent extraction of +\texttt{Criteria} will be eliminated, as the necessary information can +be determined directly from the \texttt{Scene}. Second, the source text +will be incorporated into the augmented AST in a more convenient form, +so there will no need to refer back to the source itself. + +The division of responsibilities among classes will remain largely the +same, with the major differences corresponding to the changes in the +data flow. First, the annotation locations will be read from the +\texttt{Scene} and mapped to the corresponding path in the (augmented) +AST (if any). Second, since \texttt{AAST} will incorporate the salient +capabilities of \texttt{JCTree} and \texttt{Source}, it will cover the +responsibilities of both classes. + +Many of the current supporting classes will be retained as they are. In +particular, the new AFU will continue to rely on javac's AST classes and +parser, and \texttt{scene-lib} probably will need little or no +modification. The \texttt{annotator.find} package will be radically +simplified with the elimination of the \texttt{Criterion} and +(possibly) \texttt{Insertion} hierarchies, as the insertion location +within the AST can be determined directly from the \texttt{Scene}. + +The redesign can be broken down into two independent parts: +\begin{description} +\item{Path-based positioning.} Eliminate the \texttt{Criterion} +hierarchy; determine the insertion location and the text to be inserted +during (a single) scene traversal; let the scene traversal rather than +an AST traversal be the outer loop. Advantages: since insertion is +based on the node rather than a numeric position, there is only one +positioning logic (even if there are still two matching objects); an +entire pass and a large amount of intermediate data are eliminated, thus +improving time and space efficiency. +\item{Tree transformation.} Eliminate the \texttt{Insertion} hierarchy; +instead of calculating numeric positions, apply a transformation to the +appropriate subtree. Advantage: the logic of tree transformation is +both closer to the specification and easier to comprehend (and maintain) +than the logic of finding numeric positions for inserting text by +reverse order. +\end{description} + +While these have no obvious technical disadvantages with respect to the +existing AFU, the payoff must be weighed against the costs. The current +codebase is mostly stable at this point, so any increase in reliability +will be small (though for verification software, a jump from, say, +99.99\% to 99.9999\% correct makes an enormous difference). +Fortunately, the AFU's extensive test suite will make it clear how +quickly a revised implementation achieves the same level of reliability. + +\subsection{Path-based Positioning} + +The current AFU creates transforms the scene into a set of +\texttt{Insertions}, each with its own \texttt{Criteria} (a collection +of objects of various \texttt{Criterion} subclasses). In doing so, it +throws away information about hierarchical relationships that could be +used to guide the search for matching locations in the code. + +\begin{figure}[ht] +\begin{center} +\resizebox{156pt}{!}{\includegraphics{corresp.png}} +\end{center} +\caption{ +Correspondence between scene and AST. +} +\label{fig:corresp} +\end{figure} + +It makes more sense to traverse an AST in parallel (conceptually, not +necessarily by different processors) with a scene that refers to it. +For example, if a class specification (\texttt{AClass}) in a scene +corresponds to a class node in an AST, each time one of the method +specifications (\texttt{AMethod}) is visited, the corresponding method +node (see Figure~\ref{fig:corresp}) should be visited as well to see +whether any insertions under the \texttt{AMethod} apply to it. This +strategy not only obviates an additional pass but avoids much useless +test repetition, as there is no need to test \emph{every} potential +insertion against \emph{every} AST node encountered before the correct +insertion site. With this change, it becomes possible to generate +\texttt{Insertion}s on the fly---or perhaps even to go ahead and insert +text, though managing order dependencies would take some additional +work. + +The main loop of the program thus will become a pre-order traversal of +\texttt{AScene} rather than of \texttt{JCTree}, although the scene +traversal will explore corresponding parts of the AST. In other words: +the process will be guided by the annotations to be inserted rather than +by the shape of the AST. Hence, each specified annotation will be +visited exactly once. The information currently used to create +\texttt{Criteria} for the insertions can instead be used to directly +determine the path (if any) in the AST to which the current scene +element corresponds. + +\subsection{Tree Transformation} + +The current AFU finds numeric source positions in trees and inserts +annotations directly into the source code. The position-finding logic +is therefore applied at a time other than insertion, introducing +insertion order dependencies and making it harder to find the source of +a bug. + +A new class hierarchy, the ``Augmented AST'' (\texttt{AAST}), will +directly manage non-leaf text--that is, inter-leaf text (whitespace and +comments) and intra-branch text (implicit or contained in branch node +data members). It will be an implementation of +\texttt{com.sun.source.Tree} that provides needed bits from +\texttt{com.sun.tools.javac.tree.JCTree} and defines a parallel subclass +hierarchy. AAST and subclasses provide methods to retrieve the +inter-leaf text segments that precede and follow an AST node and +printing methods that preserve the formatting of the source code. + +Non-leaf text will be stored in three types of locations in an AAST: +\begin{enumerate} +\item Each leaf node will store any non-code text that immediately +follows. +\item For branch nodes, for every implicit text segment or data member +that is represented in the source code but not in a descendant node, the +node will store any whitespace and comments that follow, unless the +content is at the end of the node's text. +\item The computation unit node will store any text that precedes the +beginning of the Java code. +\end{enumerate} +In this way, all text segment references occur exactly once in the AST, +and annotations can be inserted at the front with no need to take +preceding text into account. + +An annotation insertion will consist of a transformation of a tree node. +If no additional code is to be generated, the transformation is simple: +\begin{itemize} +\item For type annotations, the parent AST node of the element to be +annotated adds the optional annotation node if it is absent, then adds +the annotation to the annotation node. +\item For declaration annotations, the annotation is added onto the +front of a declaration's list of modifiers. +\end{itemize} +Cases that involve code generation are more complex. +Figures~\ref{fig:nocast} and \ref{fig:typecast} give an example (pre- +and post-insertion, respectively) of a transformation that requires a +typecast to be generated and inserted. Note that previously existing +non-leaf text segments remain unaltered. + +\begin{figure}[ht] +\begin{center} +\resizebox{261pt}{!}{\includegraphics{nocast.png}} +\end{center} +\caption{ +Augmented subgraph for \texttt{int i = 0}. Text of leaf nodes and text +representing elements of branch nodes are connected with solid arrows, +and inter-leaf and inter-element text segments are connected with dashed +arrows. The text of the subgraph can be read from the lower frontier of +the graph. (Underscores represent spaces here.) +} +\label{fig:nocast} +\end{figure} + +\begin{figure}[ht] +\begin{center} +\resizebox{\columnwidth}{!}{\includegraphics{typecast.png}} +\end{center} +\caption{ +Augmented subgraph for \texttt{int i = (@A int) 0}, the result of adding +an annotated cast \texttt{(@A int)} to the previous figure's code. With +respect to the graph in the previous figure, \texttt{TypeCast} has taken +\texttt{Literal}'s place in the tree, and \texttt{Literal} has become a +child of \texttt{TypeCast} along with the newly inserted material. +} +\label{fig:typecast} +\end{figure} + +The other major change will be in the output generation process, which +will depend on the \texttt{AAST} rather than directly on the source. +The \texttt{AAST}'s pretty-printer will perform an in-order traversal +and emit the text of each leaf interleaved with the non-leaf text. + +\subsection{Algorithm Summary} + +\begin{enumerate} +\item Interpret each JAIF as a ``scene'' (\texttt{AScene}). +\item Parse each Java source file to obtain a (type-checked) AST. +\item For each AST: +\begin{enumerate} +\item Transform the AST into an AAST. +\item For each annotated element in the scene in pre-order: +\begin{itemize} +\item Try to find the AAST node to which the element corresponds. +\item If the node exists (or can be generated): +\begin{itemize} +\item Transform the subtree rooted at the node to incorporate the +annotation. +\end{itemize} +\end{itemize} +\item Pretty-print AAST. +\end{enumerate} +\end{enumerate} + +\section{Planning} + +There will be some time required for integrating the two parts if they +are implemented separately. In particular, doing tree transformation +separately from path-based positioning means reifying transformations +so they can be done in a second pass. Hence it makes more sense to +do path-based positioning on the way to doing tree transformation. + +Estimated times are as follows: +\begin{itemize} +\item Path-based positioning: 2.5 weeks. +\item Tree transformation: 3 to 3.5 weeks if done after path-based +positioning, 4 weeks otherwise. +\end{itemize} +Thus, doing the full job should take about six weeks. + +\appendix + +\section{API extensions (non-exhaustive)} + +\begin{jcode} +public abstract class AAST extends com.sun.source.Tree { + /** Interleaves non-leaf text with text serialization of AAST. */ + public void prettyPrint(java.io.Writer w) throws java.io.IOException; + + /** Insert single annotation at given path. */ + public insertAnnotation(TreePath path, Annotation anno); +} + +// main loop +AAST insertAnnotations(annotations.el.AScene scene, AAST aast); +\end{jcode} + +\newpage +\section{Excerpts from original API}\label{sec:exc} + +\begin{jcode} +public interface Tree { + public enum Kind { // MUCH larger in actual interface + /** + * Used for instances of {@link BinaryTree} representing + * addition or string concatenation {@code +}. + */ + PLUS(BinaryTree.class), + /** + * Used for instances of {@link LiteralTree} representing + * an integral literal expression of type {@code int}. + */ + INT_LITERAL(LiteralTree.class); + } + Kind getKind(); + /** + * Accept method used to implement the visitor pattern. The + * visitor pattern is used to implement operations on trees. + * + * @param <R> result type of this operation. + * @param <D> type of additional data. + */ + <R,D> R accept(TreeVisitor<R,D> visitor, D data); +} +public interface ExpressionTree extends Tree {} +public interface BinaryTree extends ExpressionTree { + ExpressionTree getLeftOperand(); + ExpressionTree getRightOperand(); +} +public interface LiteralTree extends ExpressionTree { + Object getValue(); +} + +public abstract class JCTree implements Tree { + public int getStartPosition() { + return TreeInfo.getStartPos(this); + } + public static abstract class JCExpression + extends JCTree implements ExpressionTree {} + public static class JCBinary extends JCExpression implements BinaryTree { + private Tag opcode; + public JCExpression lhs; + public JCExpression rhs; + public Symbol operator; + protected JCBinary(Tag opcode, + JCExpression lhs, + JCExpression rhs, + Symbol operator) { + this.opcode = opcode; + this.lhs = lhs; + this.rhs = rhs; + this.operator = operator; + } + public Kind getKind() { return TreeInfo.tagToKind(getTag()); } + public JCExpression getLeftOperand() { return lhs; } + public JCExpression getRightOperand() { return rhs; } + public Symbol getOperator() { return operator; } + @Override + public <R,D> R accept(TreeVisitor<R,D> v, D d) { + return v.visitBinary(this, d); + } + } + public static class JCLiteral extends JCExpression implements LiteralTree { + public TypeTag typetag; + public Object value; + protected JCLiteral(TypeTag typetag, Object value) { + this.typetag = typetag; + this.value = value; + } + public Kind getKind() { + return typetag.getKindLiteral(); + } + public Object getValue() { + switch (typetag) { + case BOOLEAN: + int bi = (Integer) value; + return (bi != 0); + case CHAR: + int ci = (Integer) value; + char c = (char) ci; + if (c != ci) + throw new AssertionError("bad value for char literal"); + return c; + default: + return value; + } + } + @Override + public <R,D> R accept(TreeVisitor<R,D> v, D d) { + return v.visitLiteral(this, d); + } + } +} +\end{jcode} + +\end{document} +
diff --git a/annotation-file-utilities/figures/Makefile b/annotation-file-utilities/figures/Makefile new file mode 100644 index 0000000..5a63c61 --- /dev/null +++ b/annotation-file-utilities/figures/Makefile
@@ -0,0 +1,19 @@ +# Put user-specific changes in your own Makefile.user. +# Make will silently continue if that file does not exist. +-include Makefile.user + + +PNGFILES = corresp.png dataflow.png nocast.png typecast.png + +all: ${EPSFILES} ${PDFFILES} ${PNGFILES} png-copy + +png-copy: ${PNGFILES} + cp -pf ${PNGFILES} .. + +%.png : %.dot + dot -Tpng $< > `basename $< .dot`.png + +clean: + @\rm -f *.eps + @\rm -f *.pdf + @\rm -f *.png
diff --git a/annotation-file-utilities/figures/corresp.dot b/annotation-file-utilities/figures/corresp.dot new file mode 100644 index 0000000..ac4b50a --- /dev/null +++ b/annotation-file-utilities/figures/corresp.dot
@@ -0,0 +1,17 @@ +strict digraph corresp { +graph [ratio=1,ordering=out] +AClass +AMethod +ABlock +ClassTree [shape=box] +MethodTree [shape=box] +BlockTree [shape=box] +z1 [label="..."] +z2 [label="..."] +AClass -> AMethod -> ABlock -> z1 +ClassTree -> MethodTree -> BlockTree -> z2 +AClass -> ClassTree [style=dashed,constraint=false] +AMethod -> MethodTree [style=dashed,constraint=false] +ABlock -> BlockTree [style=dashed,constraint=false] +} +
diff --git a/annotation-file-utilities/figures/dataflow.dot b/annotation-file-utilities/figures/dataflow.dot new file mode 100644 index 0000000..de33bb0 --- /dev/null +++ b/annotation-file-utilities/figures/dataflow.dot
@@ -0,0 +1,28 @@ +strict digraph { +JAIF [shape=box] +Java [shape=box] +"Annotated Java" [shape=box] +"Augmented AST" [style=bold] +Source [constraint=false] +JAIF -> Scene +Scene -> "Annotations\nw/ AST Paths" [style=bold] +Java -> Source +Source -> AST +AST -> "Augmented AST" [style=bold] +Source -> "Augmented AST" [style=bold] +"Augmented AST" -> "Annotated AAST" [style=bold] +"Augmented AST" -> "Annotations\nw/ AST Paths" [style=bold] +"Annotation\nSpecifications" -> "Annotations\nw/ AST Paths" [style=dashed] +"Annotation\nSpecifications" -> "Annotations w/\nCode Locations" [style=dashed] +"Annotations\nw/ AST Paths" -> "Annotated AAST" +"Annotated AAST" -> "Annotated Java" +Source -> Bytecode +Bytecode -> "Annotated AAST" +Source -> "Annotated AAST" [style=dashed] +Source -> "Annotated Java" [style=dashed] +AST -> "Annotated AAST" [style=dashed] +AST -> "Annotations\nw/ AST Paths" [style=dashed] +"Annotations w/\nCode Locations" -> "Annotated AAST" [style=dashed] +Scene -> "Annotation\nSpecifications" [style=dashed] +} +
diff --git a/annotation-file-utilities/figures/nocast.dot b/annotation-file-utilities/figures/nocast.dot new file mode 100644 index 0000000..152508f --- /dev/null +++ b/annotation-file-utilities/figures/nocast.dot
@@ -0,0 +1,34 @@ +strict digraph { +graph [ordering=out] + +d [shape=box,label=LocalVariableDeclaration] +m [shape=box,label=Modifiers] +t [shape=box,label=PrimitiveTypeTree] +l [shape=box,label=Literal] +e1 [shape=plaintext,label="",width=0.125] +e2 [shape=plaintext,label="",width=0.125] +int [shape=plaintext,width=0.125] +i [shape=plaintext,constraint=false,width=0.125] +b1 [shape=plaintext,label="_",width=0.125] +b2 [shape=plaintext,label="_",constraint=false,width=0.125] +b3 [shape=plaintext,label="_",constraint=false,width=0.125] +q [shape=plaintext,label="=",constraint=false,width=0.125] +z [shape=plaintext,label="0",width=0.125] +y [shape=plaintext,label="[...]",width=0.125] + +subgraph {rank=same;int;b1;i;b2;q;b3;z;y} + +d -> m +m -> e1 +m -> e2 [style=dashed] +d -> t +t -> int +t -> b1 [style=dashed] +d -> i +d -> b2 [style=dashed] +d -> q +d -> b3 [style=dashed] +d -> l +l -> z +l -> y [style=dashed] +}
diff --git a/annotation-file-utilities/figures/scene-lib-type-hierarchy.svg b/annotation-file-utilities/figures/scene-lib-type-hierarchy.svg new file mode 100644 index 0000000..07cccd1 --- /dev/null +++ b/annotation-file-utilities/figures/scene-lib-type-hierarchy.svg
@@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd"> +<svg width="25cm" height="6cm" viewBox="-1 -1 901 201" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <g> + <rect style="fill: #ffffff" x="379" y="1" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="379" y="1" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="458.5" y="25.1333"> + <tspan x="458.5" y="25.1333">AElement</tspan> + </text> + </g> + <g> + <rect style="fill: #ffffff" x="380" y="80" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="380" y="80" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="459.5" y="104.133"> + <tspan x="459.5" y="104.133">ADeclaration</tspan> + </text> + </g> + <g> + <rect style="fill: #ffffff" x="621" y="79" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="621" y="79" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="700.5" y="103.133"> + <tspan x="700.5" y="103.133">AExpression</tspan> + </text> + </g> + <g> + <rect style="fill: #ffffff" x="740" y="159" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="740" y="159" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="819.5" y="183.133"> + <tspan x="819.5" y="183.133">ABlock</tspan> + </text> + </g> + <g> + <rect style="fill: #ffffff" x="381" y="159" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="381" y="159" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="460.5" y="183.133"> + <tspan x="460.5" y="183.133">AMethod</tspan> + </text> + </g> + <g> + <rect style="fill: #ffffff" x="140" y="81" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="140" y="81" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="219.5" y="105.133"> + <tspan x="219.5" y="105.133">ATypeElement</tspan> + </text> + </g> + <g> + <rect style="fill: #ffffff" x="1" y="159" width="179.35" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="1" y="159" width="179.35" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="90.675" y="183.133"> + <tspan x="90.675" y="183.133">ATypeElementWithType</tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="459.5" y1="80" x2="458.75" y2="50.7329"/> + <polygon style="fill: #000000" points="458.557,43.2353 463.812,53.1039 458.75,50.7329 453.815,53.3602 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="458.557,43.2353 463.812,53.1039 458.75,50.7329 453.815,53.3602 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="660.75" y1="79" x2="507.73" y2="43.2169"/> + <polygon style="fill: #000000" points="500.427,41.5092 511.303,38.9175 507.73,43.2169 509.026,48.6548 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="500.427,41.5092 511.303,38.9175 507.73,43.2169 509.026,48.6548 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="259.25" y1="81" x2="409.306" y2="43.3683"/> + <polygon style="fill: #000000" points="416.581,41.5439 408.098,48.8263 409.306,43.3683 405.665,39.1266 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="416.581,41.5439 408.098,48.8263 409.306,43.3683 405.665,39.1266 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="779.75" y1="159" x2="709.192" y2="123.387"/> + <polygon style="fill: #000000" points="702.496,120.008 713.676,120.05 709.192,123.387 709.171,128.977 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="702.496,120.008 713.676,120.05 709.192,123.387 709.171,128.977 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="460.5" y1="159" x2="459.75" y2="129.733"/> + <polygon style="fill: #000000" points="459.557,122.235 464.812,132.104 459.75,129.733 454.815,132.36 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="459.557,122.235 464.812,132.104 459.75,129.733 454.815,132.36 "/> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="135.513" y1="159" x2="210.63" y2="125.013"/> + <polygon style="fill: #000000" points="217.463,121.922 210.413,130.599 210.63,125.013 206.291,121.489 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="217.463,121.922 210.413,130.599 210.63,125.013 206.291,121.489 "/> + </g> + <g> + <rect style="fill: #ffffff" x="560.45" y="159.2" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="560.45" y="159.2" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="639.95" y="183.333"> + <tspan x="639.95" y="183.333">AField</tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="600.2" y1="159.2" x2="508.326" y2="123.524"/> + <polygon style="fill: #000000" points="501.334,120.809 512.466,119.768 508.326,123.524 508.846,129.09 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="501.334,120.809 512.466,119.768 508.326,123.524 508.846,129.09 "/> + </g> + <g> + <rect style="fill: #ffffff" x="200.9" y="159.4" width="159" height="40"/> + <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="200.9" y="159.4" width="159" height="40"/> + <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="280.4" y="183.533"> + <tspan x="280.4" y="183.533">AClass</tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="320.15" y1="159.4" x2="410.697" y2="123.581"/> + <polygon style="fill: #000000" points="417.671,120.823 410.211,129.15 410.697,123.581 406.533,119.852 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="417.671,120.823 410.211,129.15 410.697,123.581 406.533,119.852 "/> + </g> +</svg>
diff --git a/annotation-file-utilities/figures/tool-relations.svg b/annotation-file-utilities/figures/tool-relations.svg new file mode 100644 index 0000000..d2b0a3b --- /dev/null +++ b/annotation-file-utilities/figures/tool-relations.svg
@@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd"> +<svg width="8cm" height="8cm" viewBox="-1 -81 307 284" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <g> + <polygon style="fill: #ffffff" points="0,-80 52.2,-80 64.2,-68 64.2,2 0,2 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="0,-80 52.2,-80 64.2,-68 64.2,2 0,2 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="52.2,-80 52.2,-68 64.2,-68 "/> + <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="7" y="-55.1"> + <tspan x="7" y="-55.1"></tspan> + <tspan x="7" y="-39.1">.java </tspan> + <tspan x="7" y="-23.1"></tspan> + <tspan x="7" y="-7.1"></tspan> + </text> + </g> + <g> + <polygon style="fill: #ffffff" points="240,-80 292.2,-80 304.2,-68 304.2,2 240,2 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="240,-80 292.2,-80 304.2,-68 304.2,2 240,2 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="292.2,-80 292.2,-68 304.2,-68 "/> + <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="247" y="-55.1"> + <tspan x="247" y="-55.1"></tspan> + <tspan x="247" y="-39.1">.class</tspan> + <tspan x="247" y="-23.1"></tspan> + <tspan x="247" y="-7.1"></tspan> + </text> + </g> + <g> + <polygon style="fill: #ffffff" points="120,120 172.2,120 184.2,132 184.2,202 120,202 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="120,120 172.2,120 184.2,132 184.2,202 120,202 "/> + <polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="172.2,120 172.2,132 184.2,132 "/> + <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="127" y="144.9"> + <tspan x="127" y="144.9"></tspan> + <tspan x="127" y="160.9">.jaif </tspan> + <tspan x="127" y="176.9"></tspan> + <tspan x="127" y="192.9"></tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="64.2" y1="-39" x2="230.264" y2="-39"/> + <polygon style="fill: #000000" points="237.764,-39 227.764,-34 230.264,-39 227.764,-44 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="237.764,-39 227.764,-34 230.264,-39 227.764,-44 "/> + </g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="120" y1="161" x2="78.8465" y2="92.0049"/> + <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="132.177" y="-44.609"> + <tspan x="132.177" y="-44.609">javac</tspan> + </text> + <g> + <rect style="fill: none; fill-opacity:0; stroke-width: 2.35099e-37; stroke: #ffffff" x="0" y="60" width="138.6" height="32"/> + <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="69.3" y="71.9"> + <tspan x="69.3" y="71.9">insert-annotations</tspan> + <tspan x="69.3" y="87.9">-to-source</tspan> + </text> + </g> + <g> + <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="61.2624" y1="60.0112" x2="36.4729" y2="10.6988"/> + <polygon style="fill: #000000" points="33.1043,3.99784 42.0631,10.6867 36.4729,10.6988 33.1285,15.1781 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="33.1043,3.99784 42.0631,10.6867 36.4729,10.6988 33.1285,15.1781 "/> + </g> + <g> + <rect style="fill: none; fill-opacity:0; stroke-width: 2.35099e-37; stroke: #ffffff" x="215.572" y="74.687" width="84.7" height="48"/> + <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="257.922" y="94.587"> + <tspan x="257.922" y="94.587">insert-</tspan> + <tspan x="257.922" y="110.587">annotations</tspan> + </text> + </g> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 184.2,161 A 133.344,133.344 0 0 0 241.696,122.113"/> + <g> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 266.31,74.6929 A 192.351,192.351 0 0 0 272.935,11.6908"/> + <polygon style="fill: #000000" points="272.348,4.22225 278.427,13.6054 273.181,11.6759 268.489,14.7155 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="272.348,4.22225 278.427,13.6054 273.181,11.6759 268.489,14.7155 "/> + </g> + <g> + <rect style="fill: none; fill-opacity:0; stroke-width: 2.35099e-37; stroke: #ffffff" x="140" y="20" width="84.7" height="32"/> + <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="182.35" y="31.9"> + <tspan x="182.35" y="31.9">extract-</tspan> + <tspan x="182.35" y="47.9">annotations</tspan> + </text> + </g> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 272.1,2 A 185.385,185.385 0 0 0 209.031,20.008"/> + <g> + <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 173.747,51.9912 A 242.139,242.139 0 0 0 153.845,110.428"/> + <polygon style="fill: #000000" points="152.457,117.793 149.116,107.123 153.653,110.389 158.988,108.718 "/> + <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="152.457,117.793 149.116,107.123 153.653,110.389 158.988,108.718 "/> + </g> +</svg>
diff --git a/annotation-file-utilities/figures/typecast.dot b/annotation-file-utilities/figures/typecast.dot new file mode 100644 index 0000000..ca63506 --- /dev/null +++ b/annotation-file-utilities/figures/typecast.dot
@@ -0,0 +1,57 @@ +strict digraph { +graph [ordering=out] + +TypeCast [shape=box,width=1.25] +AnnotatedType [shape=box] +Annotation [shape=box] +Ident [shape=box] +PrimitiveTypeTree [shape=box] +e1 [shape=plaintext,label="",width=0.0] +e2 [shape=plaintext,label="",width=0.0] +e3 [shape=plaintext,label="",width=0.0] +b1 [shape=plaintext,label="_",width=0.0] +b2 [shape=plaintext,label="_",width=0.0] +b3 [shape=plaintext,label="_",width=0.0] +b4 [shape=plaintext,label="_",width=0.0] +b5 [shape=plaintext,label="_",width=0.0] +b6 [shape=plaintext,label="_",width=0.0] +d [shape=box,label=LocalVariableDeclaration] +m [shape=box,label=Modifiers] +t [shape=box,label=PrimitiveTypeTree] +l [shape=box,label=Literal] +i [shape=plaintext,width=0.0] +i1 [shape=plaintext,label=int,width=0.0] +i2 [shape=plaintext,label=int,width=0.0] +p1 [shape=plaintext,label="(",width=0.0] +p2 [shape=plaintext,label=")",width=0.0] +a [shape=plaintext,label="A",width=0.0] +z [shape=plaintext,label="0",width=0.0] +q [shape=plaintext,label="=",width=0.0] +y [shape=plaintext,label="[...]",width=0.0] + +subgraph {rank=same;e1;e2;i1;b1;i;b2;q;b3;TypeCast} +subgraph {rank=sink;e3;b4;b5;b6;i2;p1;p2;a;z;y} + +d -> {m, t} +m -> e1 +m -> e2 [style=dashed] +t -> i1 +t -> b1 +d -> i +d -> b2 [style=dashed] +d -> q +d -> b3 [style=dashed] +d -> TypeCast -> p1 +TypeCast -> e3 [style=dashed] +TypeCast -> AnnotatedType +AnnotatedType -> {Annotation, PrimitiveTypeTree} +Annotation -> Ident -> a +Ident -> b4 +PrimitiveTypeTree -> i2 +PrimitiveTypeTree -> b5 [style = dashed] +l -> z +l -> y [style=dashed] +TypeCast -> p2 +TypeCast -> b6 [style=dashed] +TypeCast -> l +}
diff --git a/annotation-file-utilities/lib/asm-5.0.jar b/annotation-file-utilities/lib/asm-5.0.jar new file mode 100644 index 0000000..5678cc0 --- /dev/null +++ b/annotation-file-utilities/lib/asm-5.0.jar Binary files differ
diff --git a/annotation-file-utilities/lib/guava-20.0.jar b/annotation-file-utilities/lib/guava-20.0.jar new file mode 100644 index 0000000..632772f --- /dev/null +++ b/annotation-file-utilities/lib/guava-20.0.jar Binary files differ
diff --git a/annotation-file-utilities/lib/plume-core.jar b/annotation-file-utilities/lib/plume-core.jar new file mode 100644 index 0000000..5bd7bb5 --- /dev/null +++ b/annotation-file-utilities/lib/plume-core.jar Binary files differ
diff --git a/annotation-file-utilities/scripts/extract-annotations b/annotation-file-utilities/scripts/extract-annotations new file mode 100755 index 0000000..c452d39 --- /dev/null +++ b/annotation-file-utilities/scripts/extract-annotations
@@ -0,0 +1,32 @@ +#!/bin/sh + +# Extract annotations from a class file and write them to an annotation file. +# For usage information, run: extract-annotations --help +# See the Annotation File Utilities documentation for more information. + +# If the very first argument is "--debug-script", debug this script (but +# don't pass --debug-script to the underlying program). +DEBUG=0 +if [ "$1" = "--debug-script" ]; then + DEBUG=1 + shift 1 +fi + +AFU=${AFU:-$(dirname $0)/..} +ANNOTATION_FILE_UTILS=${AFU}/bin:${AFU}/../scene-lib/bin:${AFU}/../asmx/bin:${AFU}/annotation-file-utilities.jar +LANGTOOLS=${LANGTOOLS:-${AFU}/../../jsr308-langtools} +JAVAC_JAR=${JAVAC_JAR:-${LANGTOOLS}/dist/lib/javac.jar} + +if [ "$DEBUG" = "1" ]; then + echo "--- start of extract-annotations debugging output" + echo "AFU=${AFU}" + echo "ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS}" + echo "LANGTOOLS=${LANGTOOLS}" + echo "JAVAC_JAR=${JAVAC_JAR}" + # Keep this in sync with the actual command below. + echo java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-core.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileReader "$@" + echo "--- end of extract-annotations debugging output" +fi + +# Needs CLASSPATH to find user files +java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-core.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileReader "$@"
diff --git a/annotation-file-utilities/scripts/extract-annotations.bat b/annotation-file-utilities/scripts/extract-annotations.bat new file mode 100644 index 0000000..ab1f626 --- /dev/null +++ b/annotation-file-utilities/scripts/extract-annotations.bat
@@ -0,0 +1,14 @@ + +:: Extract annotations from a class file and write them to an annotation file. +:: For usage information, run: extract-annotations.bat --help +:: See the annotation file utilities documentation for more information. + +set ANNOTATION_FILE_UTILS=%~d0 +set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%%~p0 +set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%\..\annotation-file-utilities.jar +set JAVAC_JAR=%~d0 +set JAVAC_JAR=%ANNOTATION_FILE_UTILS%%~p0 +set JAVAC_JAR=%JAVAC_JAR%..\..\..\jsr308-langtools\dist\lib\javac.jar + +java -ea -cp "%JAVAC_JAR%;%ANNOTATION_FILE_UTILS%;%CLASSPATH%" annotations.io.classfile.ClassFileReader %* +
diff --git a/annotation-file-utilities/scripts/insert-annotations b/annotation-file-utilities/scripts/insert-annotations new file mode 100755 index 0000000..d648dad --- /dev/null +++ b/annotation-file-utilities/scripts/insert-annotations
@@ -0,0 +1,32 @@ +#!/bin/sh + +# Insert annotations (from an annotation file) into a class file. +# For usage information, run: insert-annotations --help +# See the Annotation File Utilities documentation for more information. + +# If the very first argument is "--debug-script", debug this script (but +# don't pass --debug-script to the underlying program). +DEBUG=0 +if [ "$1" = "--debug-script" ]; then + DEBUG=1 + shift 1 +fi + +AFU=${AFU:-$(dirname $0)/..} +ANNOTATION_FILE_UTILS=${AFU}/bin:${AFU}/../scene-lib/bin:${AFU}/../asmx/bin:${AFU}/annotation-file-utilities.jar +LANGTOOLS=${LANGTOOLS:-${AFU}/../../jsr308-langtools} +JAVAC_JAR=${JAVAC_JAR:-${LANGTOOLS}/dist/lib/javac.jar} + +if [ "$DEBUG" = "1" ]; then + echo "--- start of insert-annotations debugging output" + echo "AFU=${AFU}" + echo "ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS}" + echo "LANGTOOLS=${LANGTOOLS}" + echo "JAVAC_JAR=${JAVAC_JAR}" + # Keep this in sync with the actual command below. + echo "java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-lib.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileWriter $@" + echo "--- end of insert-annotations debugging output" +fi + +# Needs CLASSPATH to find user files +java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-lib.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileWriter "$@"
diff --git a/annotation-file-utilities/scripts/insert-annotations-to-source b/annotation-file-utilities/scripts/insert-annotations-to-source new file mode 100755 index 0000000..ac1c50a --- /dev/null +++ b/annotation-file-utilities/scripts/insert-annotations-to-source
@@ -0,0 +1,33 @@ +#!/bin/sh + +# Insert annotations (from an annotation file) into a Java source file. +# For usage information, run: insert-annotations-to-source --help +# See the Annotation File Utilities documentation for more information. + +# If the very first argument is "--debug-script", debug this script (but +# don't pass --debug-script to the underlying program). +DEBUG=0 +if [ "$1" = "--debug-script" ]; then + DEBUG=1 + shift 1 +fi + +AFU=${AFU:-$(dirname $0)/..} +AFU=`cd \`dirname $0\`/.. && pwd` +ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS:-${AFU}/bin:${AFU}/../scene-lib/bin:${AFU}/../asmx/bin:${AFU}/annotation-file-utilities.jar} +LANGTOOLS=${LANGTOOLS:-${AFU}/../../jsr308-langtools} +JAVAC_JAR=${JAVAC_JAR:-${LANGTOOLS}/dist/lib/javac.jar} + +if [ "$DEBUG" = "1" ]; then + echo "--- start of insert-annotations-to-source debugging output" + echo "AFU=${AFU}" + echo "ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS}" + echo "LANGTOOLS=${LANGTOOLS}" + echo "JAVAC_JAR=${JAVAC_JAR}" + # Keep this in sync with the actual command below. + echo java -ea -Xmx512m -classpath ${JAVAC_JAR}:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotator.Main "$@" + echo "--- end of insert-annotations-to-source debugging output" +fi + +# Augment, don't replace, CLASSPATH, so as to find user files. +java -ea -Xmx512m -classpath ${JAVAC_JAR}:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotator.Main "$@"
diff --git a/annotation-file-utilities/scripts/insert-annotations-to-source.bat b/annotation-file-utilities/scripts/insert-annotations-to-source.bat new file mode 100644 index 0000000..26faa94 --- /dev/null +++ b/annotation-file-utilities/scripts/insert-annotations-to-source.bat
@@ -0,0 +1,12 @@ +:: Insert annotations (from an annotation file) into a Java source file. +:: For usage information, run: insert-annotations-to-source.bat --help +:: See the annotation file utilities documentation for more information. + +set ANNOTATION_FILE_UTILS=%~d0 +set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%%~p0 +set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%\..\annotation-file-utilities.jar +set JAVAC_JAR=%~d0 +set JAVAC_JAR=%ANNOTATION_FILE_UTILS%%~p0 +set JAVAC_JAR=%JAVAC_JAR%..\..\..\jsr308-langtools\dist\lib\javac.jar + +java -ea -cp "%JAVAC_JAR%;%ANNOTATION_FILE_UTILS%;%CLASSPATH%" annotator.Main %*
diff --git a/annotation-file-utilities/scripts/insert-annotations.bat b/annotation-file-utilities/scripts/insert-annotations.bat new file mode 100644 index 0000000..1ae5184 --- /dev/null +++ b/annotation-file-utilities/scripts/insert-annotations.bat
@@ -0,0 +1,13 @@ + +:: Insert annotations (from an annoation file) into a class file. +:: For usage information, run: insert-annotations.bat --help +:: See the annotation file utilities documentation for more information. + +set ANNOTATION_FILE_UTILS=%~d0 +set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%%~p0 +set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%\..\annotation-file-utilities.jar +set JAVAC_JAR=%~d0 +set JAVAC_JAR=%ANNOTATION_FILE_UTILS%%~p0 +set JAVAC_JAR=%JAVAC_JAR%..\..\..\jsr308-langtools\dist\lib\javac.jar + +java -ea -cp "%JAVAC_JAR%;%ANNOTATION_FILE_UTILS%;%CLASSPATH%" annotations.io.classfile.ClassFileWriter %*
diff --git a/annotation-file-utilities/scripts/merge-annotations b/annotation-file-utilities/scripts/merge-annotations new file mode 100755 index 0000000..f843735 --- /dev/null +++ b/annotation-file-utilities/scripts/merge-annotations
@@ -0,0 +1,40 @@ +#!/bin/sh + +# Ad hoc script for merging annotated JDKs. Arguments are JDK source +# directories, assumed to be children of $js and identified by directory +# name only (e.g. "merge-annotations nullness"). + +if [ $# -le 1 ]; then exit 0; fi + +js="$HOME/src/jdk1.8.0_45/src" +jb="$CHECKERFRAMEWORK/checker/dist/*" +cf="$CHECKERFRAMEWORK/checker/build" +sl="$JSR308/annotation-tools/scene-lib/bin" +al="$JSR308/annotation-tools/annotation-file-utilities/lib" +jj=./jdk.jaif +wd=`pwd` +cp="$jb:$cf:$sl:$al/*:$wd:$CLASSPATH" +if [ 0 -eq 1 ] ; then +td=/tmp/merge-jaifs.* +else +td=/tmp/merge-jaifs.$$ + +for aj in $* ; do + cd $aj/build + for f in `find . -name '*\.class' -print | sed 's-^\./--'` ; do + CLASSPATH="$wd/$aj/build:$CHECKERFRAMEWORK/checker/build:$CLASSPATH" \ + extract-annotations "$f" + if [ $? -eq 0 ] ; then + d=`dirname "$f"` + mkdir -p "$td/$aj/$d" + mv "$d"/*.jaif "$td/$aj/$d" + fi + done + cd "$wd" +done +fi + +find $td -type f -print | xargs java -cp "$cp" annotations.tools.IndexFileMerger $td | sed 's/(value={\[/({/g' | sed 's/\]}/}/g' > "./$jj" &&\ +find "$js" -name "*\.java" -print | CLASSPATH="$cp" xargs insert-annotations-to-source --print-error-stack "$jj" &&\ +rm -rf $td +
diff --git a/annotation-file-utilities/src/annotator/Main.java b/annotation-file-utilities/src/annotator/Main.java new file mode 100644 index 0000000..ae49953 --- /dev/null +++ b/annotation-file-utilities/src/annotator/Main.java
@@ -0,0 +1,1012 @@ +package annotator; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import plume.FileIOException; +import plume.Option; +import plume.OptionGroup; +import plume.Options; +import plume.Pair; +import plume.UtilMDE; +import type.Type; +import annotations.Annotation; +import annotations.el.ABlock; +import annotations.el.AClass; +import annotations.el.ADeclaration; +import annotations.el.AElement; +import annotations.el.AExpression; +import annotations.el.AField; +import annotations.el.AMethod; +import annotations.el.AScene; +import annotations.el.ATypeElement; +import annotations.el.ATypeElementWithType; +import annotations.el.AnnotationDef; +import annotations.el.DefException; +import annotations.el.ElementVisitor; +import annotations.el.LocalLocation; +import annotations.io.ASTIndex; +import annotations.io.ASTPath; +import annotations.io.ASTRecord; +import annotations.io.DebugWriter; +import annotations.io.IndexFileParser; +import annotations.io.IndexFileWriter; +import annotations.util.coll.VivifyingMap; +import annotator.find.AnnotationInsertion; +import annotator.find.CastInsertion; +import annotator.find.ConstructorInsertion; +import annotator.find.Criteria; +import annotator.find.GenericArrayLocationCriterion; +import annotator.find.Insertion; +import annotator.find.Insertions; +import annotator.find.NewInsertion; +import annotator.find.ReceiverInsertion; +import annotator.find.TreeFinder; +import annotator.find.TypedInsertion; +import annotator.scanner.LocalVariableScanner; +import annotator.specification.IndexFileSpecification; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.google.common.collect.SetMultimap; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; +import com.sun.tools.javac.main.CommandLine; +import com.sun.tools.javac.tree.JCTree; + +/** + * This is the main class for the annotator, which inserts annotations in + * Java source code. You can call it as <tt>java annotator.Main</tt> or by + * using the shell script <tt>insert-annotations-to-source</tt>. + * <p> + * + * It takes as input + * <ul> + * <li>annotation (index) files, which indicate the annotations to insert</li> + * <li>Java source files, into which the annotator inserts annotations</li> + * </ul> + * Annotations that are not for the specified Java files are ignored. + * <p> + * + * The <a name="command-line-options">command-line options</a> are as follows: + * <!-- start options doc (DO NOT EDIT BY HAND) --> + * <ul> + * <li id="optiongroup:General-options">General options + * <ul> + * <li id="option:outdir"><b>-d</b> <b>--outdir=</b><i>directory</i>. Directory in which output files are written. [default annotated/]</li> + * <li id="option:in-place"><b>-i</b> <b>--in-place=</b><i>boolean</i>. If true, overwrite original source files (making a backup first). + * Furthermore, if the backup files already exist, they are used instead + * of the .java files. This behavior permits a user to tweak the .jaif + * file and re-run the annotator. + * <p> + * + * Note that if the user runs the annotator with --in-place, makes edits, + * and then re-runs the annotator with this --in-place option, those + * edits are lost. Similarly, if the user runs the annotator twice in a + * row with --in-place, only the last set of annotations will appear in + * the codebase at the end. + * <p> + * + * To preserve changes when using the --in-place option, first remove the + * backup files. Or, use the <tt>-d .</tt> option, which makes (and + * reads) no backup, instead of --in-place. [default false]</li> + * <li id="option:abbreviate"><b>-a</b> <b>--abbreviate=</b><i>boolean</i>. Abbreviate annotation names [default true]</li> + * <li id="option:comments"><b>-c</b> <b>--comments=</b><i>boolean</i>. Insert annotations in comments [default false]</li> + * <li id="option:omit-annotation"><b>-o</b> <b>--omit-annotation=</b><i>string</i>. Omit given annotation</li> + * <li id="option:nowarn"><b>--nowarn=</b><i>boolean</i>. Suppress warnings about disallowed insertions [default false]</li> + * <li id="option:convert-jaifs"><b>--convert-jaifs=</b><i>boolean</i>. Convert JAIFs to new format [default false]</li> + * <li id="option:help"><b>-h</b> <b>--help=</b><i>boolean</i>. Print usage information and exit [default false]</li> + * </ul> + * </li> + * <li id="optiongroup:Debugging-options">Debugging options + * <ul> + * <li id="option:verbose"><b>-v</b> <b>--verbose=</b><i>boolean</i>. Verbose (print progress information) [default false]</li> + * <li id="option:debug"><b>--debug=</b><i>boolean</i>. Debug (print debug information) [default false]</li> + * <li id="option:print-error-stack"><b>--print-error-stack=</b><i>boolean</i>. Print error stack [default false]</li> + * </ul> + * </li> + * </ul> + * <!-- end options doc --> + */ +public class Main { + + /** Directory in which output files are written. */ + @OptionGroup("General options") + @Option("-d <directory> Directory in which output files are written") + public static String outdir = "annotated/"; + + /** + * If true, overwrite original source files (making a backup first). + * Furthermore, if the backup files already exist, they are used instead + * of the .java files. This behavior permits a user to tweak the .jaif + * file and re-run the annotator. + * <p> + * + * Note that if the user runs the annotator with --in-place, makes edits, + * and then re-runs the annotator with this --in-place option, those + * edits are lost. Similarly, if the user runs the annotator twice in a + * row with --in-place, only the last set of annotations will appear in + * the codebase at the end. + * <p> + * + * To preserve changes when using the --in-place option, first remove the + * backup files. Or, use the <tt>-d .</tt> option, which makes (and + * reads) no backup, instead of --in-place. + */ + @Option("-i Overwrite original source files") + public static boolean in_place = false; + + @Option("-a Abbreviate annotation names") + public static boolean abbreviate = true; + + @Option("-c Insert annotations in comments") + public static boolean comments = false; + + @Option("-o Omit given annotation") + public static String omit_annotation; + + @Option("Suppress warnings about disallowed insertions") + public static boolean nowarn; + + // Instead of doing insertions, create new JAIFs using AST paths + // extracted from existing JAIFs and source files they match + @Option("Convert JAIFs to AST Path format") + public static boolean convert_jaifs = false; + + @Option("-h Print usage information and exit") + public static boolean help = false; + + // Debugging options go below here. + + @OptionGroup("Debugging options") + @Option("-v Verbose (print progress information)") + public static boolean verbose; + + @Option("Debug (print debug information)") + public static boolean debug = false; + + @Option("Print error stack") + public static boolean print_error_stack = false; + + private static ElementVisitor<Void, AElement> classFilter = + new ElementVisitor<Void, AElement>() { + <K, V extends AElement> + Void filter(VivifyingMap<K, V> vm0, VivifyingMap<K, V> vm1) { + for (Map.Entry<K, V> entry : vm0.entrySet()) { + entry.getValue().accept(this, vm1.vivify(entry.getKey())); + } + return null; + } + + @Override + public Void visitAnnotationDef(AnnotationDef def, AElement el) { + // not used, since package declarations not handled here + return null; + } + + @Override + public Void visitBlock(ABlock el0, AElement el) { + ABlock el1 = (ABlock) el; + filter(el0.locals, el1.locals); + return visitExpression(el0, el); + } + + @Override + public Void visitClass(AClass el0, AElement el) { + AClass el1 = (AClass) el; + filter(el0.methods, el1.methods); + filter(el0.fields, el1.fields); + filter(el0.fieldInits, el1.fieldInits); + filter(el0.staticInits, el1.staticInits); + filter(el0.instanceInits, el1.instanceInits); + return visitDeclaration(el0, el); + } + + @Override + public Void visitDeclaration(ADeclaration el0, AElement el) { + ADeclaration el1 = (ADeclaration) el; + VivifyingMap<ASTPath, ATypeElement> insertAnnotations = + el1.insertAnnotations; + VivifyingMap<ASTPath, ATypeElementWithType> insertTypecasts = + el1.insertTypecasts; + for (Map.Entry<ASTPath, ATypeElement> entry : + el0.insertAnnotations.entrySet()) { + ASTPath p = entry.getKey(); + ATypeElement e = entry.getValue(); + insertAnnotations.put(p, e); + // visitTypeElement(e, insertAnnotations.vivify(p)); + } + for (Map.Entry<ASTPath, ATypeElementWithType> entry : + el0.insertTypecasts.entrySet()) { + ASTPath p = entry.getKey(); + ATypeElementWithType e = entry.getValue(); + type.Type type = e.getType(); + if (type instanceof type.DeclaredType + && ((type.DeclaredType) type).getName().isEmpty()) { + insertAnnotations.put(p, e); + // visitTypeElement(e, insertAnnotations.vivify(p)); + } else { + insertTypecasts.put(p, e); + // visitTypeElementWithType(e, insertTypecasts.vivify(p)); + } + } + return null; + } + + @Override + public Void visitExpression(AExpression el0, AElement el) { + AExpression el1 = (AExpression) el; + filter(el0.typecasts, el1.typecasts); + filter(el0.instanceofs, el1.instanceofs); + filter(el0.news, el1.news); + return null; + } + + @Override + public Void visitField(AField el0, AElement el) { + return visitDeclaration(el0, el); + } + + @Override + public Void visitMethod(AMethod el0, AElement el) { + AMethod el1 = (AMethod) el; + filter(el0.bounds, el1.bounds); + filter(el0.parameters, el1.parameters); + filter(el0.throwsException, el1.throwsException); + el0.returnType.accept(this, el1.returnType); + el0.receiver.accept(this, el1.receiver); + el0.body.accept(this, el1.body); + return visitDeclaration(el0, el); + } + + @Override + public Void visitTypeElement(ATypeElement el0, AElement el) { + ATypeElement el1 = (ATypeElement) el; + filter(el0.innerTypes, el1.innerTypes); + return null; + } + + @Override + public Void visitTypeElementWithType(ATypeElementWithType el0, + AElement el) { + ATypeElementWithType el1 = (ATypeElementWithType) el; + el1.setType(el0.getType()); + return visitTypeElement(el0, el); + } + + @Override + public Void visitElement(AElement el, AElement arg) { + return null; + } + }; + + private static AScene filteredScene(final AScene scene) { + final AScene filtered = new AScene(); + filtered.packages.putAll(scene.packages); + filtered.imports.putAll(scene.imports); + for (Map.Entry<String, AClass> entry : scene.classes.entrySet()) { + String key = entry.getKey(); + AClass clazz0 = entry.getValue(); + AClass clazz1 = filtered.classes.vivify(key); + clazz0.accept(classFilter, clazz1); + } + filtered.prune(); + return filtered; + } + + private static ATypeElement findInnerTypeElement(Tree t, + ASTRecord rec, ADeclaration decl, Type type, Insertion ins) { + ASTPath astPath = rec.astPath; + GenericArrayLocationCriterion galc = + ins.getCriteria().getGenericArrayLocation(); + assert astPath != null && galc != null; + List<TypePathEntry> tpes = galc.getLocation(); + ASTPath.ASTEntry entry; + for (TypePathEntry tpe : tpes) { + switch (tpe.tag) { + case ARRAY: + if (!astPath.isEmpty()) { + entry = astPath.get(-1); + if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY + && entry.childSelectorIs(ASTPath.TYPE)) { + entry = new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, + ASTPath.TYPE, entry.getArgument() + 1); + break; + } + } + entry = new ASTPath.ASTEntry(Tree.Kind.ARRAY_TYPE, + ASTPath.TYPE); + break; + case INNER_TYPE: + entry = new ASTPath.ASTEntry(Tree.Kind.MEMBER_SELECT, + ASTPath.EXPRESSION); + break; + case TYPE_ARGUMENT: + entry = new ASTPath.ASTEntry(Tree.Kind.PARAMETERIZED_TYPE, + ASTPath.TYPE_ARGUMENT, tpe.arg); + break; + case WILDCARD: + entry = new ASTPath.ASTEntry(Tree.Kind.UNBOUNDED_WILDCARD, + ASTPath.BOUND); + break; + default: + throw new IllegalArgumentException("unknown type tag " + tpe.tag); + } + astPath = astPath.extend(entry); + } + + return decl.insertAnnotations.vivify(astPath); + } + + private static void convertInsertion(String pkg, + JCTree.JCCompilationUnit tree, ASTRecord rec, Insertion ins, + AScene scene, Multimap<Insertion, Annotation> insertionSources) { + Collection<Annotation> annos = insertionSources.get(ins); + if (rec == null) { + if (ins.getCriteria().isOnPackage()) { + for (Annotation anno : annos) { + scene.packages.get(pkg).tlAnnotationsHere.add(anno); + } + } + } else if (scene != null && rec.className != null) { + AClass clazz = scene.classes.vivify(rec.className); + ADeclaration decl = null; // insertion target + if (ins.getCriteria().onBoundZero()) { + int n = rec.astPath.size(); + if (!rec.astPath.get(n-1).childSelectorIs(ASTPath.BOUND)) { + ASTPath astPath = ASTPath.empty(); + for (int i = 0; i < n; i++) { + astPath = astPath.extend(rec.astPath.get(i)); + } + astPath = astPath.extend( + new ASTPath.ASTEntry(Tree.Kind.TYPE_PARAMETER, + ASTPath.BOUND, 0)); + rec = rec.replacePath(astPath); + } + } + if (rec.methodName == null) { + decl = rec.varName == null ? clazz + : clazz.fields.vivify(rec.varName); + } else { + AMethod meth = clazz.methods.vivify(rec.methodName); + if (rec.varName == null) { + decl = meth; // ? + } else { + try { + int i = Integer.parseInt(rec.varName); + decl = i < 0 ? meth.receiver + : meth.parameters.vivify(i); + } catch (NumberFormatException e) { + TreePath path = ASTIndex.getTreePath(tree, rec); + JCTree.JCVariableDecl varTree = null; + JCTree.JCMethodDecl methTree = null; + JCTree.JCClassDecl classTree = null; + loop: + while (path != null) { + Tree leaf = path.getLeaf(); + switch (leaf.getKind()) { + case VARIABLE: + varTree = (JCTree.JCVariableDecl) leaf; + break; + case METHOD: + methTree = (JCTree.JCMethodDecl) leaf; + break; + case ANNOTATION: + case CLASS: + case ENUM: + case INTERFACE: + break loop; + default: + path = path.getParentPath(); + } + } + while (path != null) { + Tree leaf = path.getLeaf(); + Tree.Kind kind = leaf.getKind(); + if (kind == Tree.Kind.METHOD) { + methTree = (JCTree.JCMethodDecl) leaf; + int i = LocalVariableScanner.indexOfVarTree(path, + varTree, rec.varName); + int m = methTree.getStartPosition(); + int a = varTree.getStartPosition(); + int b = varTree.getEndPosition(tree.endPositions); + LocalLocation loc = new LocalLocation(i, a-m, b-a); + decl = meth.body.locals.vivify(loc); + break; + } + if (ASTPath.isClassEquiv(kind)) { + classTree = (JCTree.JCClassDecl) leaf; + // ??? + break; + } + path = path.getParentPath(); + } + } + } + } + if (decl != null) { + AElement el; + if (rec.astPath.isEmpty()) { + el = decl; + } else if (ins.getKind() == Insertion.Kind.CAST) { + annotations.el.ATypeElementWithType elem = + decl.insertTypecasts.vivify(rec.astPath); + elem.setType(((CastInsertion) ins).getType()); + el = elem; + } else { + el = decl.insertAnnotations.vivify(rec.astPath); + } + for (Annotation anno : annos) { + el.tlAnnotationsHere.add(anno); + } + if (ins instanceof TypedInsertion) { + TypedInsertion ti = (TypedInsertion) ins; + if (!rec.astPath.isEmpty()) { + // addInnerTypePaths(decl, rec, ti, insertionSources); + } + for (Insertion inner : ti.getInnerTypeInsertions()) { + Tree t = ASTIndex.getNode(tree, rec); + if (t != null) { + ATypeElement elem = findInnerTypeElement(t, + rec, decl, ti.getType(), inner); + for (Annotation a : insertionSources.get(inner)) { + elem.tlAnnotationsHere.add(a); + } + } + } + } + } + } + } + + + // Implementation details: + // 1. The annotator partially compiles source + // files using the compiler API (JSR-199), obtaining an AST. + // 2. The annotator reads the specification file, producing a set of + // annotator.find.Insertions. Insertions completely specify what to + // write (as a String, which is ultimately translated according to the + // keyword file) and how to write it (as annotator.find.Criteria). + // 3. It then traverses the tree, looking for nodes that satisfy the + // Insertion Criteria, translating the Insertion text against the + // keyword file, and inserting the annotations into the source file. + + /** + * Runs the annotator, parsing the source and spec files and applying + * the annotations. + */ + public static void main(String[] args) throws IOException { + + if (verbose) { + System.out.printf("insert-annotations-to-source (%s)", + annotations.io.classfile.ClassFileReader.INDEX_UTILS_VERSION); + } + + Options options = new Options( + "Main [options] { jaif-file | java-file | @arg-file } ...\n" + + "(Contents of argfiles are expanded into the argument list.)", + Main.class); + String[] file_args; + try { + String[] cl_args = CommandLine.parse(args); + file_args = options.parse_or_usage(cl_args); + } catch (IOException ex) { + System.err.println(ex); + System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\"."); + System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'."); + System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)"); + file_args = null; // Eclipse compiler issue workaround + System.exit(1); + } + + DebugWriter dbug = new DebugWriter(); + DebugWriter verb = new DebugWriter(); + DebugWriter both = dbug.or(verb); + dbug.setEnabled(debug); + verb.setEnabled(verbose); + TreeFinder.warn.setEnabled(!nowarn); + TreeFinder.stak.setEnabled(print_error_stack); + TreeFinder.dbug.setEnabled(debug); + Criteria.dbug.setEnabled(debug); + + if (help) { + options.print_usage(); + System.exit(0); + } + + if (in_place && outdir != "annotated/") { // interned + options.print_usage("The --outdir and --in-place options are mutually exclusive."); + System.exit(1); + } + + if (file_args.length < 2) { + options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length); + System.exit(1); + } + + // The insertions specified by the annotation files. + Insertions insertions = new Insertions(); + // The Java files into which to insert. + List<String> javafiles = new ArrayList<String>(); + + // Indices to maintain insertion source traces. + Map<String, Multimap<Insertion, Annotation>> insertionIndex = + new HashMap<String, Multimap<Insertion, Annotation>>(); + Map<Insertion, String> insertionOrigins = new HashMap<Insertion, String>(); + Map<String, AScene> scenes = new HashMap<String, AScene>(); + + IndexFileParser.setAbbreviate(abbreviate); + for (String arg : file_args) { + if (arg.endsWith(".java")) { + javafiles.add(arg); + } else if (arg.endsWith(".jaif") || + arg.endsWith(".jann")) { + IndexFileSpecification spec = new IndexFileSpecification(arg); + try { + List<Insertion> parsedSpec = spec.parse(); + AScene scene = spec.getScene(); + Collections.sort(parsedSpec, new Comparator<Insertion>() { + @Override + public int compare(Insertion i1, Insertion i2) { + ASTPath p1 = i1.getCriteria().getASTPath(); + ASTPath p2 = i2.getCriteria().getASTPath(); + return p1 == null + ? p2 == null ? 0 : -1 + : p2 == null ? 1 : p1.compareTo(p2); + } + }); + if (convert_jaifs) { + scenes.put(arg, filteredScene(scene)); + for (Insertion ins : parsedSpec) { + insertionOrigins.put(ins, arg); + } + if (!insertionIndex.containsKey(arg)) { + insertionIndex.put(arg, + LinkedHashMultimap.<Insertion, Annotation>create()); + } + insertionIndex.get(arg).putAll(spec.insertionSources()); + } + both.debug("Read %d annotations from %s%n", parsedSpec.size(), arg); + if (omit_annotation != null) { + List<Insertion> filtered = + new ArrayList<Insertion>(parsedSpec.size()); + for (Insertion insertion : parsedSpec) { + // TODO: this won't omit annotations if the insertion is more than + // just the annotation (such as if the insertion is a cast + // insertion or a 'this' parameter in a method declaration). + if (! omit_annotation.equals(insertion.getText())) { + filtered.add(insertion); + } + } + parsedSpec = filtered; + both.debug("After filtering: %d annotations from %s%n", + parsedSpec.size(), arg); + } + insertions.addAll(parsedSpec); + } catch (RuntimeException e) { + if (e.getCause() != null + && e.getCause() instanceof FileNotFoundException) { + System.err.println("File not found: " + arg); + System.exit(1); + } else { + throw e; + } + } catch (FileIOException e) { + // Add 1 to the line number since line numbers in text editors are usually one-based. + System.err.println("Error while parsing annotation file " + arg + " at line " + + (e.lineNumber + 1) + ":"); + if (e.getMessage() != null) { + System.err.println('\t' + e.getMessage()); + } + if (e.getCause() != null && e.getCause().getMessage() != null) { + System.err.println('\t' + e.getCause().getMessage()); + } + if (print_error_stack) { + e.printStackTrace(); + } + System.exit(1); + } + } else { + throw new Error("Unrecognized file extension: " + arg); + } + } + + if (dbug.isEnabled()) { + dbug.debug("%d insertions, %d .java files%n", + insertions.size(), javafiles.size()); + dbug.debug("Insertions:%n"); + for (Insertion insertion : insertions) { + dbug.debug(" %s%n", insertion); + } + } + + for (String javafilename : javafiles) { + verb.debug("Processing %s%n", javafilename); + + File javafile = new File(javafilename); + File unannotated = new File(javafilename + ".unannotated"); + if (in_place) { + // It doesn't make sense to check timestamps; + // if the .java.unannotated file exists, then just use it. + // A user can rename that file back to just .java to cause the + // .java file to be read. + if (unannotated.exists()) { + verb.debug("Renaming %s to %s%n", unannotated, javafile); + boolean success = unannotated.renameTo(javafile); + if (! success) { + throw new Error(String.format("Failed renaming %s to %s", + unannotated, javafile)); + } + } + } + + String fileSep = System.getProperty("file.separator"); + String fileLineSep = System.getProperty("line.separator"); + Source src; + // Get the source file, and use it to obtain parse trees. + try { + // fileLineSep is set here so that exceptions can be caught + fileLineSep = UtilMDE.inferLineSeparator(javafilename); + src = new Source(javafilename); + verb.debug("Parsed %s%n", javafilename); + } catch (Source.CompilerException e) { + e.printStackTrace(); + return; + } catch (IOException e) { + e.printStackTrace(); + return; + } + + // Imports required to resolve annotations (when abbreviate==true). + LinkedHashSet<String> imports = new LinkedHashSet<String>(); + int num_insertions = 0; + String pkg = ""; + + for (CompilationUnitTree cut : src.parse()) { + JCTree.JCCompilationUnit tree = (JCTree.JCCompilationUnit) cut; + ExpressionTree pkgExp = cut.getPackageName(); + pkg = pkgExp == null ? "" : pkgExp.toString(); + + // Create a finder, and use it to get positions. + TreeFinder finder = new TreeFinder(tree); + SetMultimap<Pair<Integer, ASTPath>, Insertion> positions = + finder.getPositions(tree, insertions); + + if (convert_jaifs) { + // program used only for JAIF conversion; execute following + // block and then skip remainder of loop + Multimap<ASTRecord, Insertion> astInsertions = + finder.getPaths(); + for (Map.Entry<ASTRecord, Collection<Insertion>> entry : + astInsertions.asMap().entrySet()) { + ASTRecord rec = entry.getKey(); + for (Insertion ins : entry.getValue()) { + if (ins.getCriteria().getASTPath() != null) { continue; } + String arg = insertionOrigins.get(ins); + AScene scene = scenes.get(arg); + Multimap<Insertion, Annotation> insertionSources = + insertionIndex.get(arg); + // String text = + // ins.getText(comments, abbreviate, false, 0, '\0'); + + // TODO: adjust for missing end of path (?) + + if (insertionSources.containsKey(ins)) { + convertInsertion(pkg, tree, rec, ins, scene, insertionSources); + } + } + } + continue; + } + + // Apply the positions to the source file. + if (both.isEnabled()) { + System.err.printf( + "getPositions returned %d positions in tree for %s%n", + positions.size(), javafilename); + } + + Set<Pair<Integer, ASTPath>> positionKeysUnsorted = + positions.keySet(); + Set<Pair<Integer, ASTPath>> positionKeysSorted = + new TreeSet<Pair<Integer, ASTPath>>( + new Comparator<Pair<Integer, ASTPath>>() { + @Override + public int compare(Pair<Integer, ASTPath> p1, + Pair<Integer, ASTPath> p2) { + int c = Integer.compare(p2.a, p1.a); + if (c == 0) { + c = p2.b == null ? p1.b == null ? 0 : -1 + : p1.b == null ? 1 : p2.b.compareTo(p1.b); + } + return c; + } + }); + positionKeysSorted.addAll(positionKeysUnsorted); + for (Pair<Integer, ASTPath> pair : positionKeysSorted) { + boolean receiverInserted = false; + boolean newInserted = false; + boolean constructorInserted = false; + Set<String> seen = new TreeSet<String>(); + List<Insertion> toInsertList = new ArrayList<Insertion>(positions.get(pair)); + Collections.reverse(toInsertList); + dbug.debug("insertion pos: %d%n", pair.a); + assert pair.a >= 0 + : "pos is negative: " + pair.a + " " + toInsertList.get(0) + " " + javafilename; + for (Insertion iToInsert : toInsertList) { + // Possibly add whitespace after the insertion + String trailingWhitespace = ""; + boolean gotSeparateLine = false; + int pos = pair.a; // reset each iteration in case of dyn adjustment + if (iToInsert.getSeparateLine()) { + // System.out.printf("getSeparateLine=true for insertion at pos %d: %s%n", pos, iToInsert); + int indentation = 0; + while ((pos - indentation != 0) + // horizontal whitespace + && (src.charAt(pos-indentation-1) == ' ' + || src.charAt(pos-indentation-1) == '\t')) { + // System.out.printf("src.charAt(pos-indentation-1 == %d-%d-1)='%s'%n", + // pos, indentation, src.charAt(pos-indentation-1)); + indentation++; + } + if ((pos - indentation == 0) + // horizontal whitespace + || (src.charAt(pos-indentation-1) == '\f' + || src.charAt(pos-indentation-1) == '\n' + || src.charAt(pos-indentation-1) == '\r')) { + trailingWhitespace = fileLineSep + src.substring(pos-indentation, pos); + gotSeparateLine = true; + } + } + + char precedingChar; + if (pos != 0) { + precedingChar = src.charAt(pos - 1); + } else { + precedingChar = '\0'; + } + + if (iToInsert.getKind() == Insertion.Kind.ANNOTATION) { + AnnotationInsertion ai = (AnnotationInsertion) iToInsert; + if (ai.isGenerateBound()) { // avoid multiple ampersands + try { + String s = src.substring(pos, pos+9); + if ("Object & ".equals(s)) { + ai.setGenerateBound(false); + precedingChar = '.'; // suppress leading space + } + } catch (StringIndexOutOfBoundsException e) {} + } + if (ai.isGenerateExtends()) { // avoid multiple "extends" + try { + String s = src.substring(pos, pos+9); + if (" extends ".equals(s)) { + ai.setGenerateExtends(false); + pos += 8; + } + } catch (StringIndexOutOfBoundsException e) {} + } + } else if (iToInsert.getKind() == Insertion.Kind.CAST) { + ((CastInsertion) iToInsert) + .setOnArrayLiteral(src.charAt(pos) == '{'); + } else if (iToInsert.getKind() == Insertion.Kind.RECEIVER) { + ReceiverInsertion ri = (ReceiverInsertion) iToInsert; + ri.setAnnotationsOnly(receiverInserted); + receiverInserted = true; + } else if (iToInsert.getKind() == Insertion.Kind.NEW) { + NewInsertion ni = (NewInsertion) iToInsert; + ni.setAnnotationsOnly(newInserted); + newInserted = true; + } else if (iToInsert.getKind() == Insertion.Kind.CONSTRUCTOR) { + ConstructorInsertion ci = (ConstructorInsertion) iToInsert; + if (constructorInserted) { ci.setAnnotationsOnly(true); } + constructorInserted = true; + } + + String toInsert = iToInsert.getText(comments, abbreviate, + gotSeparateLine, pos, precedingChar) + trailingWhitespace; + if (seen.contains(toInsert)) { continue; } // eliminate duplicates + seen.add(toInsert); + + // If it's already there, don't re-insert. This is a hack! + // Also, I think this is already checked when constructing the + // insertions. + int precedingTextPos = pos-toInsert.length()-1; + if (precedingTextPos >= 0) { + String precedingTextPlusChar + = src.getString().substring(precedingTextPos, pos); + if (toInsert.equals( + precedingTextPlusChar.substring(0, toInsert.length())) + || toInsert.equals(precedingTextPlusChar.substring(1))) { + dbug.debug( + "Inserting %s at %d in code of length %d with preceding text '%s'%n", + toInsert, pos, src.getString().length(), + precedingTextPlusChar); + dbug.debug("Already present, skipping%n"); + continue; + } + } + + // TODO: Neither the above hack nor this check should be + // necessary. Find out why re-insertions still occur and + // fix properly. + if (iToInsert.getInserted()) { continue; } + src.insert(pos, toInsert); + if (verbose && !debug) { + System.out.print("."); + num_insertions++; + if ((num_insertions % 50) == 0) { + System.out.println(); // terminate the line that contains dots + } + } + dbug.debug("Post-insertion source: %n" + src.getString()); + + Set<String> packageNames = iToInsert.getPackageNames(); + if (!packageNames.isEmpty()) { + dbug.debug("Need import %s%n due to insertion %s%n", + packageNames, toInsert); + imports.addAll(packageNames); + } + } + } + } + + if (convert_jaifs) { + for (Map.Entry<String, AScene> entry : scenes.entrySet()) { + String filename = entry.getKey(); + AScene scene = entry.getValue(); + try { + IndexFileWriter.write(scene, filename + ".converted"); + } catch (DefException e) { + System.err.println(filename + ": " + " format error in conversion"); + if (print_error_stack) { + e.printStackTrace(); + } + } + } + return; // done with conversion + } + + if (dbug.isEnabled()) { + dbug.debug("%d imports to insert%n", imports.size()); + for (String classname : imports) { + dbug.debug(" %s%n", classname); + } + } + + // insert import statements + { + Pattern importPattern = Pattern.compile("(?m)^import\\b"); + Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)"); + int importIndex = 0; // default: beginning of file + String srcString = src.getString(); + Matcher m = importPattern.matcher(srcString); + Set<String> inSource = new TreeSet<String>(); + if (m.find()) { + importIndex = m.start(); + do { + int i = m.start(); + int j = srcString.indexOf(System.lineSeparator(), i) + 1; + if (j <= 0) { + j = srcString.length(); + } + String s = srcString.substring(i, j); + inSource.add(s); + } while (m.find()); + } else { + // Debug.info("Didn't find import in " + srcString); + m = packagePattern.matcher(srcString); + if (m.find()) { + importIndex = m.end(); + } + } + for (String classname : imports) { + String toInsert = "import " + classname + ";" + fileLineSep; + if (!inSource.contains(toInsert)) { + inSource.add(toInsert); + src.insert(importIndex, toInsert); + importIndex += toInsert.length(); + } + } + } + + // Write the source file. + File outfile = null; + try { + if (in_place) { + outfile = javafile; + if (verbose) { + System.out.printf("Renaming %s to %s%n", javafile, unannotated); + } + boolean success = javafile.renameTo(unannotated); + if (! success) { + throw new Error(String.format("Failed renaming %s to %s", + javafile, unannotated)); + } + } else { + if (pkg.isEmpty()) { + outfile = new File(outdir, javafile.getName()); + } else { + String[] pkgPath = pkg.split("\\."); + StringBuilder sb = new StringBuilder(outdir); + for (int i = 0 ; i < pkgPath.length ; i++) { + sb.append(fileSep).append(pkgPath[i]); + } + outfile = new File(sb.toString(), javafile.getName()); + } + outfile.getParentFile().mkdirs(); + } + OutputStream output = new FileOutputStream(outfile); + if (verbose) { + System.out.printf("Writing %s%n", outfile); + } + src.write(output); + output.close(); + } catch (IOException e) { + System.err.println("Problem while writing file " + outfile); + e.printStackTrace(); + System.exit(1); + } + } + } + + public static String pathToString(TreePath path) { + if (path == null) { + return "null"; + } + return treeToString(path.getLeaf()); + } + + public static String treeToString(Tree node) { + String asString = node.toString(); + String oneLine = firstLine(asString); + return "\"" + oneLine + "\""; + } + + /** + * Return the first non-empty line of the string, adding an ellipsis + * (...) if the string was truncated. + */ + public static String firstLine(String s) { + while (s.startsWith("\n")) { + s = s.substring(1); + } + int newlineIndex = s.indexOf('\n'); + if (newlineIndex == -1) { + return s; + } else { + return s.substring(0, newlineIndex) + "..."; + } + } + + /** + * Separates the annotation class from its arguments. + * + * @return given <code>@foo(bar)</code> it returns the pair <code>{ @foo, (bar) }</code>. + */ + public static Pair<String,String> removeArgs(String s) { + int pidx = s.indexOf("("); + return (pidx == -1) ? + Pair.of(s, (String)null) : + Pair.of(s.substring(0, pidx), s.substring(pidx)); + } +}
diff --git a/annotation-file-utilities/src/annotator/Source.java b/annotation-file-utilities/src/annotator/Source.java new file mode 100644 index 0000000..b3cae71 --- /dev/null +++ b/annotation-file-utilities/src/annotator/Source.java
@@ -0,0 +1,194 @@ +package annotator; + +import java.io.*; +import java.util.*; + +import javax.tools.*; +import javax.tools.JavaCompiler.CompilationTask; + +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.util.JavacTask; +import com.sun.tools.javac.api.JavacTaskImpl; +import com.sun.tools.javac.code.Types; + +/** + * Represents a Java source file. This class provides three major operations: + * parsing the source file to obtain a syntax tree (via JSR-199), inserting text + * into the source file at specified offsets, and writing the rewritten source + * file. + */ +public final class Source { + + private JavaCompiler compiler; + private StandardJavaFileManager fileManager; + private JavacTask task; + private StringBuilder source; + private DiagnosticCollector<JavaFileObject> diagnostics; + private String path; + private Types types; + + /** + * Signifies that a problem has occurred with the compiler that produces + * the syntax tree for this source file. + */ + public static class CompilerException extends Exception { + + private static final long serialVersionUID = -4751611137146719789L; + + public CompilerException(String message) { + super(message); + } + } + + /** + * Sets up a compiler for parsing the given Java source file. + * + * @throws CompilerException if the input file couldn't be read + */ + public Source(String src) throws CompilerException, IOException { + + // Get the JSR-199 compiler. + this.compiler = javax.tools.ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + throw new CompilerException("could not get compiler instance"); + } + + diagnostics = new DiagnosticCollector<JavaFileObject>(); + + // Get the file manager for locating input files. + this.fileManager = compiler.getStandardFileManager(diagnostics, null, null); + if (fileManager == null) { + throw new CompilerException("could not get file manager"); + } + + Iterable<? extends JavaFileObject> fileObjs = fileManager + .getJavaFileObjectsFromStrings(Collections.singletonList(src)); + + // Compiler options. + // -Xlint:-options is a hack to get around Jenkins build problem: + // "target value 1.8 is obsolete and will be removed in a future release" + final String[] stringOpts = new String[] { "-g", "-Xlint:-options" }; + // "-XDTA:noannotationsincomments" + // TODO: figure out if these options are necessary? "-source", "1.6x" + List<String> optsList = Arrays.asList(stringOpts); + + // Create a task. + // This seems to require that the file names end in .java + CompilationTask cTask = + compiler.getTask(null, fileManager, diagnostics, optsList, null, fileObjs); + if (!(cTask instanceof JavacTask)) { + throw new CompilerException("could not get a valid JavacTask: " + cTask.getClass()); + } + this.task = (JavacTask)cTask; + this.types = Types.instance(((JavacTaskImpl)cTask).getContext()); + + // Read the source file into a buffer. + path = src; + source = new StringBuilder(); + FileInputStream in = new FileInputStream(src); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + int c; + while ((c = in.read()) != -1) { + bytes.write(c); + } + in.close(); + source.append(bytes.toString()); + bytes.close(); + fileManager.close(); + } + + /** + * @return an object that provides utility methods for types + */ + public Types getTypes() { return types; } + + /** + * Parse the input file, returning a set of Tree API roots (as + * <code>CompilationUnitTree</code>s). + * + * @return the Tree API roots for the input file + */ + public Set<CompilationUnitTree> parse() { + + try { + Set<CompilationUnitTree> compUnits = new HashSet<CompilationUnitTree>(); + + for (CompilationUnitTree tree : task.parse()) { + compUnits.add(tree); + } + + List<Diagnostic<? extends JavaFileObject>> errors = diagnostics.getDiagnostics(); + if (!diagnostics.getDiagnostics().isEmpty()) { + int numErrors = 0; + for (Diagnostic<? extends JavaFileObject> d : errors) { + System.err.println(d); + if (d.getKind() == Diagnostic.Kind.ERROR) { ++numErrors; } + } + if (numErrors > 0) { + System.err.println(numErrors + " error" + (numErrors != 1 ? "s" : "")); + System.err.println("WARNING: Error processing input source files. Please fix and try again."); + System.exit(1); + } + } + + // Add type information to the AST. + try { + task.analyze(); + } catch (Throwable e) { + System.err.println("WARNING: " + path + + ": type analysis failed; skipping"); + System.err.println("(incomplete CLASSPATH?)"); + return Collections.<CompilationUnitTree>emptySet(); + } + + return compUnits; + + } catch (IOException e) { + e.printStackTrace(); + throw new Error(e); + } + + // return Collections.<CompilationUnitTree>emptySet(); + } + + // TODO: Can be a problem if offsets get thrown off by previous insertions? + /** + * Inserts the given string into the source file at the given offset. + * <p> + * + * Note that calling this can throw off indices in later parts of the + * file. Therefore, when doing multiple insertions, you should perform + * them from the end of the file forward. + * + * @param offset the offset to place the start of the insertion text + * @param str the text to insert + */ + public void insert(int offset, String str) { + source.insert(offset, str); + } + + public char charAt(int index) { + return source.charAt(index); + } + + public String substring(int start, int end) { + return source.substring(start, end); + } + + public String getString() { + return source.toString(); + } + + /** + * Writes the modified source file to the given stream. + * + * @param out the stream for writing the file + * @throws IOException if the source file couldn't be written + */ + public void write(OutputStream out) throws IOException { + out.write(source.toString().getBytes()); + out.flush(); + out.close(); + } + +}
diff --git a/annotation-file-utilities/src/annotator/find/ASTPathCriterion.java b/annotation-file-utilities/src/annotator/find/ASTPathCriterion.java new file mode 100644 index 0000000..d30844f --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ASTPathCriterion.java
@@ -0,0 +1,1149 @@ +package annotator.find; + +import java.util.ArrayList; +import java.util.List; + +import javax.lang.model.element.Modifier; +import javax.lang.model.type.TypeKind; + +import annotations.io.ASTPath; +import annotator.Main; + +import com.sun.source.tree.AnnotatedTypeTree; +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.ArrayAccessTree; +import com.sun.source.tree.ArrayTypeTree; +import com.sun.source.tree.AssertTree; +import com.sun.source.tree.AssignmentTree; +import com.sun.source.tree.BinaryTree; +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.CaseTree; +import com.sun.source.tree.CatchTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompoundAssignmentTree; +import com.sun.source.tree.ConditionalExpressionTree; +import com.sun.source.tree.DoWhileLoopTree; +import com.sun.source.tree.EnhancedForLoopTree; +import com.sun.source.tree.ExpressionStatementTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.ForLoopTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.IfTree; +import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.LabeledStatementTree; +import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.MemberReferenceTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.ParameterizedTypeTree; +import com.sun.source.tree.ParenthesizedTree; +import com.sun.source.tree.ReturnTree; +import com.sun.source.tree.StatementTree; +import com.sun.source.tree.SwitchTree; +import com.sun.source.tree.SynchronizedTree; +import com.sun.source.tree.ThrowTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.TryTree; +import com.sun.source.tree.TypeCastTree; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.tree.UnaryTree; +import com.sun.source.tree.UnionTypeTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.tree.WhileLoopTree; +import com.sun.source.tree.WildcardTree; +import com.sun.source.tree.Tree.Kind; +import com.sun.source.util.SimpleTreeVisitor; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.tree.JCTree; + +/** + * A criterion to determine if a node matches a path through the AST. + */ +public class ASTPathCriterion implements Criterion { + + public static boolean debug = Main.debug; + + /** + * The path through the AST to match. + */ + ASTPath astPath; + + /** + * Constructs a new ASTPathCriterion to match the given AST path. + * <p> + * This assumes that the astPath is valid. Specifically, that all of its + * arguments have been previously validated. + * + * @param astPath + * the AST path to match + */ + public ASTPathCriterion(ASTPath astPath) { + this.astPath = astPath; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + // actualPath stores the path through the source code AST to this + // location (specified by the "path" parameter to this method). It is + // computed by traversing from this location up the source code AST + // until it reaches a method node (this gets only the part of the path + // within a method) or class node (this gets only the part of the path + // within a field). + List<Tree> actualPath = new ArrayList<Tree>(); + Tree leaf = path.getLeaf(); + Tree.Kind kind = leaf.getKind(); + while (kind != Tree.Kind.METHOD && !ASTPath.isClassEquiv(kind)) { + actualPath.add(0, leaf); + path = path.getParentPath(); + if (path == null) { break; } + leaf = path.getLeaf(); + kind = leaf.getKind(); + } + + // If astPath starts with Method.* or Class.*, include the + // MethodTree or ClassTree on actualPath. + if (path != null && !astPath.isEmpty()) { + Tree.Kind entryKind = astPath.get(0).getTreeKind(); + if (entryKind == Tree.Kind.METHOD + && kind == Tree.Kind.METHOD + || entryKind == Tree.Kind.CLASS + && ASTPath.isClassEquiv(kind)) { + actualPath.add(0, leaf); + } + } + + if (debug) { + System.out.println("ASTPathCriterion.isSatisfiedBy"); + System.out.println(" " + astPath); + for (Tree t : actualPath) { + System.out.println(" " + t.getKind() + ": " + + t.toString().replace('\n', ' ')); + } + } + + int astPathLen = astPath.size(); + int actualPathLen = actualPath.size(); + if (astPathLen == 0 || actualPathLen == 0) { return false; } + // if (actualPathLen != astPathLen + (isOnNewArrayType ? 0 : 1)) { + // return false; + // } + + Tree next = null; + int i = 0; + while (true) { + ASTPath.ASTEntry astNode = astPath.get(i); + Tree actualNode = actualPath.get(i); + if (!kindsMatch(astNode.getTreeKind(), actualNode.getKind())) { + return isBoundableWildcard(actualPath, i); + } + + if (debug) { + System.out.println("astNode: " + astNode); + System.out.println("actualNode: " + actualNode.getKind()); + } + + // Based on the child selector and (optional) argument in "astNode", + // "next" will get set to the next source node below "actualNode". + // Then "next" will be compared with the node following "astNode" + // in "actualPath". If it's not a match, this is not the correct + // location. If it is a match, keep going. + next = getNext(actualNode, astPath, i); + if (next == null) { + return checkNull(actualPath, i); + } + if (!(next instanceof JCTree)) { + // converted from array type, not in source AST... + if (actualPathLen == i+1) { + // need to extend actualPath with "artificial" node + actualPath.add(next); + ++actualPathLen; + } + } + if (debug) { + System.out.println("next: " + next); + } + + // if (++i >= astPathLen || i >= actualPathLen) { break; } + if (++i >= astPathLen) { break; } + if (i >= actualPathLen) { + return checkNull(actualPath, i-1); + } + if (!matchNext(next, actualPath.get(i))) { + if (debug) { + System.out.println("no next match"); + } + return false; + } + } + + if (i < actualPathLen && matchNext(next, actualPath.get(i)) + || i <= actualPathLen + && next.getKind() == Tree.Kind.NEW_ARRAY) { + return true; + } + + if (debug) { + System.out.println("no next match"); + } + return false; + } + + private boolean matchNext(Tree next, Tree node) { + boolean b1 = next instanceof JCTree; + boolean b2 = node instanceof JCTree; + if (b1 && !b2) { + next = Insertions.TypeTree.fromJCTree((JCTree) next); + } else if (b2 && !b1) { + node = Insertions.TypeTree.fromJCTree((JCTree) node); + } + + try { + return next.accept(new SimpleTreeVisitor<Boolean, Tree>() { + @Override + public Boolean + defaultAction(Tree t1, Tree t2) { + return t1 == t2; + } + + @Override + public Boolean + visitIdentifier(IdentifierTree v, Tree t) { + return v == t; + // IdentifierTree i2 = (IdentifierTree) t; + // return i1.getName().toString() + // .equals(i2.getName().toString()); + } + + @Override + public Boolean + visitAnnotatedType(AnnotatedTypeTree a1, Tree t) { + AnnotatedTypeTree a2 = (AnnotatedTypeTree) t; + return matchNext(a1.getUnderlyingType(), + a2.getUnderlyingType()); + } + + // @Override + // public Boolean + // visitArrayType(ArrayTypeTree b1, Tree t) { + // ArrayTypeTree b2 = (ArrayTypeTree) t; + // return matchNext(b1.getType(), b2.getType()); + // } + + @Override + public Boolean + visitMemberSelect(MemberSelectTree c1, Tree t) { + MemberSelectTree c2 = (MemberSelectTree) t; + return c1.getIdentifier().toString() + .equals(c2.getIdentifier().toString()) + && matchNext(c1.getExpression(), + c2.getExpression()); + } + + @Override + public Boolean + visitWildcard(WildcardTree d1, Tree t) { + return d1 == (WildcardTree) t; + // WildcardTree d2 = (WildcardTree) t; + // Tree bound2 = d2.getBound(); + // Tree bound1 = d1.getBound(); + // return bound1 == bound2 || matchNext(bound1, bound2); + } + + @Override + public Boolean + visitParameterizedType(ParameterizedTypeTree e1, Tree t) { + ParameterizedTypeTree e2 = (ParameterizedTypeTree) t; + List<? extends Tree> l2 = e2.getTypeArguments(); + List<? extends Tree> l1 = e1.getTypeArguments(); + if (l1.size() == l2.size()) { + int i = 0; + for (Tree t1 : l1) { + Tree t2 = l2.get(i++); + if (!matchNext(t1, t2)) { return false; } + } + return matchNext(e1.getType(), e2.getType()); + } + return false; + } + }, node); + } catch (RuntimeException ex) { + return false; + } + } + + private Tree getNext(Tree actualNode, ASTPath astPath, int ix) { + try { + ASTPath.ASTEntry astNode = astPath.get(ix); + switch (actualNode.getKind()) { + case ANNOTATED_TYPE: { + AnnotatedTypeTree annotatedType = + (AnnotatedTypeTree) actualNode; + if (astNode.childSelectorIs(ASTPath.ANNOTATION)) { + int arg = astNode.getArgument(); + List<? extends AnnotationTree> annos = + annotatedType.getAnnotations(); + if (arg >= annos.size()) { + return null; + } + return annos.get(arg); + } else { + return annotatedType.getUnderlyingType(); + } + } + case ARRAY_ACCESS: { + ArrayAccessTree arrayAccess = (ArrayAccessTree) actualNode; + if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { + return arrayAccess.getExpression(); + } else { + return arrayAccess.getIndex(); + } + } + case ARRAY_TYPE: { + ArrayTypeTree arrayType = (ArrayTypeTree) actualNode; + return arrayType.getType(); + } + case ASSERT: { + AssertTree azzert = (AssertTree) actualNode; + if (astNode.childSelectorIs(ASTPath.CONDITION)) { + return azzert.getCondition(); + } else { + return azzert.getDetail(); + } + } + case ASSIGNMENT: { + AssignmentTree assignment = (AssignmentTree) actualNode; + if (astNode.childSelectorIs(ASTPath.VARIABLE)) { + return assignment.getVariable(); + } else { + return assignment.getExpression(); + } + } + case BLOCK: { + BlockTree block = (BlockTree) actualNode; + int arg = astNode.getArgument(); + List<? extends StatementTree> statements = block.getStatements(); + if (arg >= block.getStatements().size()) { + return null; + } + return statements.get(arg); + } + case CASE: { + CaseTree caze = (CaseTree) actualNode; + if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { + return caze.getExpression(); + } else { + int arg = astNode.getArgument(); + List<? extends StatementTree> statements = caze.getStatements(); + if (arg >= statements.size()) { + return null; + } + return statements.get(arg); + } + } + case CATCH: { + CatchTree cach = (CatchTree) actualNode; + if (astNode.childSelectorIs(ASTPath.PARAMETER)) { + return cach.getParameter(); + } else { + return cach.getBlock(); + } + } + case ANNOTATION: + case CLASS: + case ENUM: + case INTERFACE: { + ClassTree clazz = (ClassTree) actualNode; + int arg = astNode.getArgument(); + if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) { + return clazz.getTypeParameters().get(arg); + } else if (astNode.childSelectorIs(ASTPath.INITIALIZER)) { + int i = 0; + for (Tree member : clazz.getMembers()) { + if (member.getKind() == Tree.Kind.BLOCK && arg == i++) { + return member; + } + } + return null; + } else if (astNode.childSelectorIs(ASTPath.BOUND)) { + return arg < 0 ? clazz.getExtendsClause() + : clazz.getImplementsClause().get(arg); + } else { + return null; + } + } + case CONDITIONAL_EXPRESSION: { + ConditionalExpressionTree conditionalExpression = (ConditionalExpressionTree) actualNode; + if (astNode.childSelectorIs(ASTPath.CONDITION)) { + return conditionalExpression.getCondition(); + } else if (astNode.childSelectorIs(ASTPath.TRUE_EXPRESSION)) { + return conditionalExpression.getTrueExpression(); + } else { + return conditionalExpression.getFalseExpression(); + } + } + case DO_WHILE_LOOP: { + DoWhileLoopTree doWhileLoop = (DoWhileLoopTree) actualNode; + if (astNode.childSelectorIs(ASTPath.CONDITION)) { + return doWhileLoop.getCondition(); + } else { + return doWhileLoop.getStatement(); + } + } + case ENHANCED_FOR_LOOP: { + EnhancedForLoopTree enhancedForLoop = (EnhancedForLoopTree) actualNode; + if (astNode.childSelectorIs(ASTPath.VARIABLE)) { + return enhancedForLoop.getVariable(); + } else if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { + return enhancedForLoop.getExpression(); + } else { + return enhancedForLoop.getStatement(); + } + } + case EXPRESSION_STATEMENT: { + ExpressionStatementTree expressionStatement = (ExpressionStatementTree) actualNode; + return expressionStatement.getExpression(); + } + case FOR_LOOP: { + ForLoopTree forLoop = (ForLoopTree) actualNode; + if (astNode.childSelectorIs(ASTPath.INITIALIZER)) { + int arg = astNode.getArgument(); + List<? extends StatementTree> inits = forLoop.getInitializer(); + if (arg >= inits.size()) { + return null; + } + return inits.get(arg); + } else if (astNode.childSelectorIs(ASTPath.CONDITION)) { + return forLoop.getCondition(); + } else if (astNode.childSelectorIs(ASTPath.UPDATE)) { + int arg = astNode.getArgument(); + List<? extends ExpressionStatementTree> updates = forLoop.getUpdate(); + if (arg >= updates.size()) { + return null; + } + return updates.get(arg); + } else { + return forLoop.getStatement(); + } + } + case IF: { + IfTree iff = (IfTree) actualNode; + if (astNode.childSelectorIs(ASTPath.CONDITION)) { + return iff.getCondition(); + } else if (astNode.childSelectorIs(ASTPath.THEN_STATEMENT)) { + return iff.getThenStatement(); + } else { + return iff.getElseStatement(); + } + } + case INSTANCE_OF: { + InstanceOfTree instanceOf = (InstanceOfTree) actualNode; + if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { + return instanceOf.getExpression(); + } else { + return instanceOf.getType(); + } + } + case LABELED_STATEMENT: { + LabeledStatementTree labeledStatement = (LabeledStatementTree) actualNode; + return labeledStatement.getStatement(); + } + case LAMBDA_EXPRESSION: { + LambdaExpressionTree lambdaExpression = (LambdaExpressionTree) actualNode; + if (astNode.childSelectorIs(ASTPath.PARAMETER)) { + int arg = astNode.getArgument(); + List<? extends VariableTree> params = lambdaExpression.getParameters(); + if (arg >= params.size()) { + return null; + } + return params.get(arg); + } else { + return lambdaExpression.getBody(); + } + } + case MEMBER_REFERENCE: { + MemberReferenceTree memberReference = (MemberReferenceTree) actualNode; + if (astNode.childSelectorIs(ASTPath.QUALIFIER_EXPRESSION)) { + return memberReference.getQualifierExpression(); + } else { + int arg = astNode.getArgument(); + List<? extends ExpressionTree> typeArgs = memberReference.getTypeArguments(); + if (arg >= typeArgs.size()) { + return null; + } + return typeArgs.get(arg); + } + } + case MEMBER_SELECT: { + MemberSelectTree memberSelect = (MemberSelectTree) actualNode; + return memberSelect.getExpression(); + } + case METHOD: { + MethodTree method = (MethodTree) actualNode; + if (astNode.childSelectorIs(ASTPath.TYPE)) { + return method.getReturnType(); + } else if (astNode.childSelectorIs(ASTPath.PARAMETER)) { + int arg = astNode.getArgument(); + List<? extends VariableTree> params = + method.getParameters(); + return arg < 0 ? method.getReceiverParameter() + : arg < params.size() ? params.get(arg) : null; + } else if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) { + int arg = astNode.getArgument(); + return method.getTypeParameters().get(arg); + } else { // BODY + return method.getBody(); + } + } + case METHOD_INVOCATION: { + MethodInvocationTree methodInvocation = (MethodInvocationTree) actualNode; + if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) { + int arg = astNode.getArgument(); + List<? extends Tree> typeArgs = methodInvocation.getTypeArguments(); + if (arg >= typeArgs.size()) { + return null; + } + return typeArgs.get(arg); + } else if (astNode.childSelectorIs(ASTPath.METHOD_SELECT)) { + return methodInvocation.getMethodSelect(); + } else { + int arg = astNode.getArgument(); + List<? extends ExpressionTree> args = methodInvocation.getArguments(); + if (arg >= args.size()) { + return null; + } + return args.get(arg); + } + } + case NEW_ARRAY: { + NewArrayTree newArray = (NewArrayTree) actualNode; + if (astNode.childSelectorIs(ASTPath.TYPE)) { + Type type = ((JCTree.JCNewArray) newArray).type; + Tree typeTree = Insertions.TypeTree.fromType(type); + int arg = astNode.getArgument(); + if (arg == 0 && astPath.size() == ix+1) { + return newArray; + // if (astPath.size() != ix+1) { return null; } + // return typeTree; + // return ((ArrayTypeTree) typeTree).getType(); + // return newArray; + } + typeTree = ((NewArrayTree) typeTree).getType(); + while (--arg > 0) { + if (typeTree.getKind() != Tree.Kind.ARRAY_TYPE) { + return null; + } + typeTree = ((ArrayTypeTree) typeTree).getType(); + } + return typeTree; + } else if (astNode.childSelectorIs(ASTPath.DIMENSION)) { + int arg = astNode.getArgument(); + List<? extends ExpressionTree> dims = newArray.getDimensions(); + return arg < dims.size() ? dims.get(arg) : null; + } else { + int arg = astNode.getArgument(); + List<? extends ExpressionTree> inits = + newArray.getInitializers(); + return arg < inits.size() ? inits.get(arg) : null; + } + } + case NEW_CLASS: { + NewClassTree newClass = (NewClassTree) actualNode; + if (astNode.childSelectorIs(ASTPath.ENCLOSING_EXPRESSION)) { + return newClass.getEnclosingExpression(); + } else if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) { + int arg = astNode.getArgument(); + List<? extends Tree> typeArgs = newClass.getTypeArguments(); + if (arg >= typeArgs.size()) { + return null; + } + return typeArgs.get(arg); + } else if (astNode.childSelectorIs(ASTPath.IDENTIFIER)) { + return newClass.getIdentifier(); + } else if (astNode.childSelectorIs(ASTPath.ARGUMENT)) { + int arg = astNode.getArgument(); + List<? extends ExpressionTree> args = newClass.getArguments(); + if (arg >= args.size()) { + return null; + } + return args.get(arg); + } else { + return newClass.getClassBody(); // For anonymous classes + } + } + case PARAMETERIZED_TYPE: { + ParameterizedTypeTree parameterizedType = (ParameterizedTypeTree) actualNode; + if (astNode.childSelectorIs(ASTPath.TYPE)) { + return parameterizedType.getType(); + } else { + int arg = astNode.getArgument(); + List<? extends Tree> typeArgs = parameterizedType.getTypeArguments(); + if (arg >= typeArgs.size()) { + return null; + } + return typeArgs.get(arg); + } + } + case PARENTHESIZED: { + ParenthesizedTree parenthesized = (ParenthesizedTree) actualNode; + return parenthesized.getExpression(); + } + case RETURN: { + ReturnTree returnn = (ReturnTree) actualNode; + return returnn.getExpression(); + } + case SWITCH: { + SwitchTree zwitch = (SwitchTree) actualNode; + if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { + return zwitch.getExpression(); + } else { + int arg = astNode.getArgument(); + List<? extends CaseTree> cases = zwitch.getCases(); + if (arg >= cases.size()) { + return null; + } + return cases.get(arg); + } + } + case SYNCHRONIZED: { + SynchronizedTree synchronizzed = (SynchronizedTree) actualNode; + if (astNode.childSelectorIs(ASTPath.EXPRESSION)) { + return synchronizzed.getExpression(); + } else { + return synchronizzed.getBlock(); + } + } + case THROW: { + ThrowTree throww = (ThrowTree) actualNode; + return throww.getExpression(); + } + case TRY: { + TryTree tryy = (TryTree) actualNode; + if (astNode.childSelectorIs(ASTPath.BLOCK)) { + return tryy.getBlock(); + } else if (astNode.childSelectorIs(ASTPath.CATCH)) { + int arg = astNode.getArgument(); + List<? extends CatchTree> catches = tryy.getCatches(); + if (arg >= catches.size()) { + return null; + } + return catches.get(arg); + } else if (astNode.childSelectorIs(ASTPath.FINALLY_BLOCK)) { + return tryy.getFinallyBlock(); + } else { + int arg = astNode.getArgument(); + List<? extends Tree> resources = tryy.getResources(); + if (arg >= resources.size()) { + return null; + } + return resources.get(arg); + } + } + case TYPE_CAST: { + TypeCastTree typeCast = (TypeCastTree) actualNode; + if (astNode.childSelectorIs(ASTPath.TYPE)) { + return typeCast.getType(); + } else { + return typeCast.getExpression(); + } + } + case TYPE_PARAMETER: { + TypeParameterTree typeParam = (TypeParameterTree) actualNode; + List<? extends Tree> bounds = typeParam.getBounds(); + int arg = astNode.getArgument(); + return bounds.get(arg); + } + case UNION_TYPE: { + UnionTypeTree unionType = (UnionTypeTree) actualNode; + int arg = astNode.getArgument(); + List<? extends Tree> typeAlts = unionType.getTypeAlternatives(); + if (arg >= typeAlts.size()) { + return null; + } + return typeAlts.get(arg); + } + case VARIABLE: { + // A VariableTree can have modifiers, but we only look at + // the initializer and type because modifiers can't be + // annotated. Any annotations on the LHS must be on the type. + VariableTree var = (VariableTree) actualNode; + if (astNode.childSelectorIs(ASTPath.INITIALIZER)) { + return var.getInitializer(); + } else if (astNode.childSelectorIs(ASTPath.TYPE)) { + return var.getType(); + } else { + return null; + } + } + case WHILE_LOOP: { + WhileLoopTree whileLoop = (WhileLoopTree) actualNode; + if (astNode.childSelectorIs(ASTPath.CONDITION)) { + return whileLoop.getCondition(); + } else { + return whileLoop.getStatement(); + } + } + default: { + if (ASTPath.isBinaryOperator(actualNode.getKind())) { + BinaryTree binary = (BinaryTree) actualNode; + if (astNode.childSelectorIs(ASTPath.LEFT_OPERAND)) { + return binary.getLeftOperand(); + } else { + return binary.getRightOperand(); + } + } else if (ASTPath.isCompoundAssignment(actualNode.getKind())) { + CompoundAssignmentTree compoundAssignment = + (CompoundAssignmentTree) actualNode; + if (astNode.childSelectorIs(ASTPath.VARIABLE)) { + return compoundAssignment.getVariable(); + } else { + return compoundAssignment.getExpression(); + } + } else if (ASTPath.isUnaryOperator(actualNode.getKind())) { + UnaryTree unary = (UnaryTree) actualNode; + return unary.getExpression(); + } else if (isWildcard(actualNode.getKind())) { + WildcardTree wildcard = (WildcardTree) actualNode; + return wildcard.getBound(); + } else { + throw new IllegalArgumentException("Illegal kind: " + + actualNode.getKind()); + } + } + } + } catch (RuntimeException ex) { return null; } + } + + private boolean checkNull(List<Tree> path, int ix) { + Tree node = path.get(path.size()-1); + int last = astPath.size() - 1; + ASTPath.ASTEntry entry = astPath.get(ix); + Tree.Kind kind = entry.getTreeKind(); + + switch (kind) { + // case ANNOTATION: + // case INTERFACE: + case CLASS: // "extends" clause? + return ASTPath.isClassEquiv(kind) + && ix == last && entry.getArgument() == -1 + && entry.childSelectorIs(ASTPath.BOUND); + case TYPE_PARAMETER: + return node.getKind() == Tree.Kind.TYPE_PARAMETER + && ix == last && entry.getArgument() == 0 + && entry.childSelectorIs(ASTPath.BOUND); + case METHOD: // nullary constructor? receiver? + if (node.getKind() != Tree.Kind.METHOD) { return false; } + MethodTree method = (MethodTree) node; + List<? extends VariableTree> params = method.getParameters(); + if ("<init>".equals(method.getName().toString())) { + if (ix == last) { return true; } + ASTPath.ASTEntry next = astPath.get(++ix); + String selector = next.getChildSelector(); + Tree typeTree = + ASTPath.TYPE_PARAMETER.equals(selector) + ? method.getTypeParameters().get(next.getArgument()) + : ASTPath.PARAMETER.equals(selector) + ? params.get(next.getArgument()).getType() + : null; + return typeTree != null && checkTypePath(ix, typeTree); + } else if (entry.childSelectorIs(ASTPath.PARAMETER) + && entry.getArgument() == -1) { + if (ix == last) { return true; } + VariableTree rcvrParam = method.getReceiverParameter(); + if (rcvrParam == null) { // TODO + // ClassTree clazz = methodReceiverType(path); + // return checkReceiverType(ix, + // ((JCTree.JCClassDecl) clazz).type); + } else { + return checkTypePath(ix+1, rcvrParam.getType()); + } + } + return false; + case NEW_ARRAY: + if (node.getKind() != Tree.Kind.NEW_ARRAY) { return false; } + NewArrayTree newArray = (NewArrayTree) node; + int arg = entry.getArgument(); + if (entry.childSelectorIs(ASTPath.TYPE)) { + if (ix == last) { return true; } + // Tree t = newArray.getType(); + // int depth = 1; + // while (t.getKind() == Tree.Kind.ARRAY_TYPE) { + // t = ((ArrayTypeTree) t).getType(); + // ++depth; + // } + return arg == arrayDepth(newArray); + } else { + List<? extends ExpressionTree> typeTrees = + entry.childSelectorIs(ASTPath.DIMENSION) + ? newArray.getDimensions() + : entry.childSelectorIs(ASTPath.INITIALIZER) + ? newArray.getInitializers() + : null; + return typeTrees != null && arg < typeTrees.size() + && checkTypePath(ix+1, typeTrees.get(arg)); + } + case UNBOUNDED_WILDCARD: + return isBoundableWildcard(path, path.size()-1); + default: // TODO: casts? + return false; + } + } + + private static int arrayDepth(Tree tree) { + if (tree.getKind() == Tree.Kind.NEW_ARRAY) { + NewArrayTree newArray = (NewArrayTree) tree; + Tree type = newArray.getType(); + if (type != null) { + return type.accept(new SimpleTreeVisitor<Integer, Integer>() { + @Override + public Integer visitArrayType(ArrayTypeTree t, Integer i) { + return t.getType().accept(this, i+1); + } + @Override + public Integer defaultAction(Tree t, Integer i) { + return i; + } + }, 1); + } + int depth = newArray.getDimensions().size(); + for (ExpressionTree elem : newArray.getInitializers()) { + Tree.Kind kind = elem.getKind(); + if (kind == Tree.Kind.NEW_ARRAY + || kind == Tree.Kind.ARRAY_TYPE) { + depth = Math.max(depth, arrayDepth(elem)+1); + } + } + return depth; + } else if (tree.getKind() == Tree.Kind.ANNOTATED_TYPE) { + return arrayDepth(((AnnotatedTypeTree) tree).getUnderlyingType()); + } else if (tree.getKind() == Tree.Kind.ARRAY_TYPE) { + return 1 + arrayDepth(((ArrayTypeTree) tree).getType()); + } else { + return 0; + } + } + + private boolean checkReceiverType(int i, Type t) { + if (t == null) { return false; } + while (++i < astPath.size()) { + ASTPath.ASTEntry entry = astPath.get(i); + switch (entry.getTreeKind()) { + case ANNOTATED_TYPE: + break; + case ARRAY_TYPE: + if (t.getKind() != TypeKind.ARRAY) { return false; } + t = ((Type.ArrayType) t).getComponentType(); + break; + case MEMBER_SELECT: + // TODO + break; + case PARAMETERIZED_TYPE: + if (entry.childSelectorIs(ASTPath.TYPE_PARAMETER)) { + if (!t.isParameterized()) { return false; } + List<Type> args = t.getTypeArguments(); + int a = entry.getArgument(); + if (a >= args.size()) { return false; } + t = args.get(a); + } // else TYPE -- stay? + break; + case TYPE_PARAMETER: + if (t.getKind() != TypeKind.WILDCARD) { return false; } + t = t.getLowerBound(); + break; + case EXTENDS_WILDCARD: + if (t.getKind() != TypeKind.WILDCARD) { return false; } + t = ((Type.WildcardType) t).getExtendsBound(); + break; + case SUPER_WILDCARD: + if (t.getKind() != TypeKind.WILDCARD) { return false; } + t = ((Type.WildcardType) t).getSuperBound(); + break; + case UNBOUNDED_WILDCARD: + if (t.getKind() != TypeKind.WILDCARD) { return false; } + t = t.getLowerBound(); + break; + default: + return false; + } + if (t == null) { return false; } + } + return true; + } + + private static ClassTree methodReceiverType(TreePath path) { + Tree t = path.getLeaf(); + if (t.getKind() != Tree.Kind.METHOD) { return null; } + JCTree.JCMethodDecl method = (JCTree.JCMethodDecl) t; + if ((method.mods.flags & Flags.STATIC) != 0) { return null; } + + // Find the name of the class with type parameters to create the + // receiver. Walk up the tree and pick up class names to add to + // the receiver type. Since we're starting from the innermost + // class, the classes we get to at earlier iterations of the loop + // are inside of the classes we get to at later iterations. + TreePath parent = path.getParentPath(); + Tree leaf = parent.getLeaf(); + Tree.Kind kind = leaf.getKind(); + + // For an inner class constructor, the receiver comes from the + // superclass, so skip past the first type definition. + boolean skip = method.getReturnType() == null; + + while (kind != Tree.Kind.COMPILATION_UNIT + && kind != Tree.Kind.NEW_CLASS) { + if (kind == Tree.Kind.CLASS + || kind == Tree.Kind.INTERFACE + || kind == Tree.Kind.ENUM + || kind == Tree.Kind.ANNOTATION_TYPE) { + JCTree.JCClassDecl clazz = (JCTree.JCClassDecl) leaf; + boolean isStatic = kind == Tree.Kind.INTERFACE + || kind == Tree.Kind.ENUM + || clazz.getModifiers().getFlags().contains(Modifier.STATIC); + skip &= !isStatic; + if (!skip || isStatic) { return clazz; } + skip = false; + } + + parent = path.getParentPath(); + leaf = parent.getLeaf(); + kind = leaf.getKind(); + } + + throw new IllegalArgumentException("no receiver for non-inner constructor"); + } + + private boolean checkTypePath(int i, Tree typeTree) { + try { +loop: while (typeTree != null && i < astPath.size()) { + ASTPath.ASTEntry entry = astPath.get(i); + Tree.Kind kind = entry.getTreeKind(); + switch (kind) { + case ANNOTATED_TYPE: + typeTree = ((AnnotatedTypeTree) typeTree) + .getUnderlyingType(); + continue; + case ARRAY_TYPE: + typeTree = ((ArrayTypeTree) typeTree).getType(); + break; + case MEMBER_SELECT: + typeTree = ((MemberSelectTree) typeTree).getExpression(); + break; + case PARAMETERIZED_TYPE: + if (entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) { + int arg = entry.getArgument(); + typeTree = ((ParameterizedTypeTree) typeTree) + .getTypeArguments().get(arg); + } else { // TYPE + typeTree = ((ParameterizedTypeTree) typeTree).getType(); + } + break; + default: + if (isWildcard(kind)) { + return ++i == astPath.size(); // ??? + } + break loop; + } + ++i; + } + } catch (RuntimeException ex) {} + return false; + } + + /** + * Determines if the given kinds match, false otherwise. Two kinds match if + * they're exactly the same or if the two kinds are both compound + * assignments, unary operators, binary operators or wildcards. + * <p> + * This is necessary because in the JAIF file these kinds are represented by + * their general types (i.e. BinaryOperator, CompoundOperator, etc.) rather + * than their kind (i.e. PLUS, MINUS, PLUS_ASSIGNMENT, XOR_ASSIGNMENT, + * etc.). Internally, a single kind is used to represent each general type + * (i.e. PLUS is used for BinaryOperator, PLUS_ASSIGNMENT is used for + * CompoundAssignment, etc.). Yet, the actual source nodes have the correct + * kind. So if an AST path entry has a PLUS kind, that really means it could + * be any BinaryOperator, resulting in PLUS matching any other + * BinaryOperator. + * + * @param kind1 + * the first kind to match + * @param kind2 + * the second kind to match + * @return {@code true} if the kinds match as described above, {@code false} + * otherwise. + */ + private boolean kindsMatch(Tree.Kind kind1, Tree.Kind kind2) { + return kind1 == kind2 ? true + : ASTPath.isClassEquiv(kind1) + ? ASTPath.isClassEquiv(kind2) + : ASTPath.isCompoundAssignment(kind1) + ? ASTPath.isCompoundAssignment(kind2) + : ASTPath.isUnaryOperator(kind1) + ? ASTPath.isUnaryOperator(kind2) + : ASTPath.isBinaryOperator(kind1) + ? ASTPath.isBinaryOperator(kind2) + : ASTPath.isWildcard(kind1) + ? ASTPath.isWildcard(kind2) + : false; + } + + /** + * Determines if the given kind is a binary operator. + * + * @param kind + * the kind to test + * @return true if the given kind is a binary operator + */ + public boolean isBinaryOperator(Tree.Kind kind) { + return kind == Tree.Kind.MULTIPLY || kind == Tree.Kind.DIVIDE + || kind == Tree.Kind.REMAINDER || kind == Tree.Kind.PLUS + || kind == Tree.Kind.MINUS || kind == Tree.Kind.LEFT_SHIFT + || kind == Tree.Kind.RIGHT_SHIFT + || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT + || kind == Tree.Kind.LESS_THAN + || kind == Tree.Kind.GREATER_THAN + || kind == Tree.Kind.LESS_THAN_EQUAL + || kind == Tree.Kind.GREATER_THAN_EQUAL + || kind == Tree.Kind.EQUAL_TO || kind == Tree.Kind.NOT_EQUAL_TO + || kind == Tree.Kind.AND || kind == Tree.Kind.XOR + || kind == Tree.Kind.OR || kind == Tree.Kind.CONDITIONAL_AND + || kind == Tree.Kind.CONDITIONAL_OR; + } + + public boolean isExpression(Tree.Kind kind) { + switch (kind) { + case ARRAY_ACCESS: + case ASSIGNMENT: + case CONDITIONAL_EXPRESSION: + case EXPRESSION_STATEMENT: + case MEMBER_SELECT: + case MEMBER_REFERENCE: + case IDENTIFIER: + case INSTANCE_OF: + case METHOD_INVOCATION: + case NEW_ARRAY: + case NEW_CLASS: + case LAMBDA_EXPRESSION: + case PARENTHESIZED: + case TYPE_CAST: + case POSTFIX_INCREMENT: + case POSTFIX_DECREMENT: + case PREFIX_INCREMENT: + case PREFIX_DECREMENT: + case UNARY_PLUS: + case UNARY_MINUS: + case BITWISE_COMPLEMENT: + case LOGICAL_COMPLEMENT: + case MULTIPLY: + case DIVIDE: + case REMAINDER: + case PLUS: + case MINUS: + case LEFT_SHIFT: + case RIGHT_SHIFT: + case UNSIGNED_RIGHT_SHIFT: + case LESS_THAN: + case GREATER_THAN: + case LESS_THAN_EQUAL: + case GREATER_THAN_EQUAL: + case EQUAL_TO: + case NOT_EQUAL_TO: + case AND: + case XOR: + case OR: + case CONDITIONAL_AND: + case CONDITIONAL_OR: + case MULTIPLY_ASSIGNMENT: + case DIVIDE_ASSIGNMENT: + case REMAINDER_ASSIGNMENT: + case PLUS_ASSIGNMENT: + case MINUS_ASSIGNMENT: + case LEFT_SHIFT_ASSIGNMENT: + case RIGHT_SHIFT_ASSIGNMENT: + case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT: + case AND_ASSIGNMENT: + case XOR_ASSIGNMENT: + case OR_ASSIGNMENT: + case INT_LITERAL: + case LONG_LITERAL: + case FLOAT_LITERAL: + case DOUBLE_LITERAL: + case BOOLEAN_LITERAL: + case CHAR_LITERAL: + case STRING_LITERAL: + case NULL_LITERAL: + return true; + default: + return false; + } + } + + /** + * Determines if the given kind is a wildcard. + * + * @param kind + * the kind to test + * @return true if the given kind is a wildcard + */ + private boolean isWildcard(Tree.Kind kind) { + return kind == Tree.Kind.UNBOUNDED_WILDCARD + || kind == Tree.Kind.EXTENDS_WILDCARD + || kind == Tree.Kind.SUPER_WILDCARD; + } + + // The following check is necessary because Oracle has decided that + // x instanceof Class<? extends Object> + // will remain illegal even though it means the same thing as + // x instanceof Class<?>. + private boolean isBoundableWildcard(List<Tree> actualPath, int i) { + if (i <= 0) { return false; } + Tree actualNode = actualPath.get(i); + if (actualNode.getKind() == Tree.Kind.UNBOUNDED_WILDCARD) { + // isWildcard(actualNode.getKind()) + // TODO: refactor GenericArrayLoc to use same code? + Tree ancestor = actualPath.get(i-1); + if (ancestor.getKind() == Tree.Kind.INSTANCE_OF) { + TreeFinder.warn.debug("WARNING: wildcard bounds not allowed " + + "in 'instanceof' expression; skipping insertion%n"); + return false; + } else if (i > 1 && ancestor.getKind() == + Tree.Kind.PARAMETERIZED_TYPE) { + ancestor = actualPath.get(i-2); + if (ancestor.getKind() == Tree.Kind.ARRAY_TYPE) { + TreeFinder.warn.debug("WARNING: wildcard bounds not allowed " + + "in 'instanceof' expression; skipping insertion%n"); + return false; + } + } + return true; + } + return false; + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.AST_PATH; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "ASTPathCriterion: " + astPath; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/AnnotationInsertion.java b/annotation-file-utilities/src/annotator/find/AnnotationInsertion.java new file mode 100644 index 0000000..2e28b87 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/AnnotationInsertion.java
@@ -0,0 +1,141 @@ +package annotator.find; + +import plume.Pair; + +/** + * Specifies an annotation to be inserted into a source file. + */ +public class AnnotationInsertion extends Insertion { + + /** + * The annotation to insert. + */ + private final String annotation; + private String type; + private boolean generateBound; + private boolean generateExtends; + private boolean wasGenerateExtends; + + /** + * Creates a new insertion. + * + * @param annotation the annotation to insert, which starts with "@" + * @param criteria where to insert the annotation + * @param separateLine whether to insert the annotation on its own + */ + public AnnotationInsertion(String annotation, Criteria criteria, boolean separateLine) { + super(criteria, separateLine); + this.annotation = annotation; + type = null; + generateBound = false; + generateExtends = false; + wasGenerateExtends = false; + } + + /** + * Creates a new insertion with an empty criteria and the text inserted on + * the same line. + * + * @param annotation the text to insert + */ + public AnnotationInsertion(String annotation) { + this(annotation, new Criteria(), false); + } + + public boolean isGenerateExtends() { + return generateExtends; + } + + public boolean isGenerateBound() { + return generateBound; + } + + public void setGenerateExtends(boolean generateExtends) { + this.generateExtends = generateExtends; + this.wasGenerateExtends |= generateExtends; + } + + public void setGenerateBound(boolean b) { + generateBound = b; + } + + /** + * Gets the insertion text. + * + * @param comments + * if true, the annotation will be surrounded by block comments + * @param abbreviate + * if true, the package name will be removed from the annotation. + * The package name can be retrieved again by calling the + * {@link #getPackageName()} method. + * @return the text to insert + */ + protected String getText(boolean comments, boolean abbreviate) { + String result = annotation; + if (abbreviate) { + Pair<String, String> ps = removePackage(result); + String packageName = ps.a; + if (packageName != null) { + packageNames.add(packageName); + result = ps.b; + } + } + if (!result.startsWith("@")) { + throw new Error("Illegal insertion, must start with @: " + result); + } + + // We insert a "new " when annotating a variable initializer that is a + // bare array expression (e.g., as in "int[] a = {0, 1};") Since the + // syntax doesn't permit adding the type annotation in front of the + // expression, we generate the explicit "new" + // (as in "int[] a = new int[] {0, 1}") to provide a legal insertion site. + + if (type != null) { + result = "new " + result + " " + type; + } else if (generateBound) { + result += " Object &"; + } else if (generateExtends) { + result = " extends " + result + " Object"; + } + return comments ? "/*" + result + "*/" : result; + } + + /** + * Gets the raw, unmodified annotation that was passed into the constructor. + * @return the annotation + */ + public String getAnnotation() { + return annotation; + } + + /** {@inheritDoc} */ + protected boolean addLeadingSpace(boolean gotSeparateLine, int pos, + char precedingChar) { + if (generateExtends || precedingChar == '.') { + return false; + } + return super.addLeadingSpace(gotSeparateLine, pos, precedingChar); + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean addTrailingSpace(boolean gotSeparateLine) { + // Never add a trailing space on a type parameter bound. + return !wasGenerateExtends && super.addTrailingSpace(gotSeparateLine); + } + + /** {@inheritDoc} */ + public Kind getKind() { + return Kind.ANNOTATION; + } + + public String toString() { + return annotation + " " + super.toString(); + } + + public void setType(String s) { + this.type = s; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/BoundLocationCriterion.java b/annotation-file-utilities/src/annotator/find/BoundLocationCriterion.java new file mode 100644 index 0000000..84abb70 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/BoundLocationCriterion.java
@@ -0,0 +1,138 @@ +package annotator.find; + +import java.util.List; + +import annotations.el.BoundLocation; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.tree.JCTree.JCExpression; + +public class BoundLocationCriterion implements Criterion { + + private Criterion parentCriterion; + private final int boundIndex; + private final int paramIndex; + + + public BoundLocationCriterion(BoundLocation boundLoc) { + this(boundLoc.boundIndex, boundLoc.paramIndex); + } + + private BoundLocationCriterion(int boundIndex, int paramIndex) { + this.boundIndex = boundIndex; + this.paramIndex = paramIndex; + + if (boundIndex != -1) { + this.parentCriterion = new BoundLocationCriterion(-1, paramIndex); + } else if (paramIndex != -1) { + this.parentCriterion = null; + } + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Tree leaf = path.getLeaf(); + + // System.out.printf("BoundLocationCriterion.isSatisfiedBy(%s):%n leaf=%s (%s)%n", path, leaf, leaf.getClass()); + + TreePath parentPath = path.getParentPath(); + if (parentPath == null) { + return false; + } + + Tree parent = parentPath.getLeaf(); + if (parent == null) { + return false; + } + + boolean returnValue = false; + + // System.out.printf("BoundLocationCriterion.isSatisfiedBy(%s):%n leaf=%s (%s)%n parent=%s (%s)%n", path, leaf, leaf.getClass(), parent, parent.getClass()); + + // if boundIndex is not null, need to check that this is right bound + // in parent + if (boundIndex != -1) { + if (parent instanceof TypeParameterTree) { + List<? extends Tree> bounds = ((TypeParameterTree) parent).getBounds(); + int ix = boundIndex; + if (!bounds.isEmpty() && isInterface((JCExpression) bounds.get(0))) { + --ix; + } + if (ix < 0 || ix < bounds.size() && bounds.get(ix) == leaf) { + returnValue = parentCriterion.isSatisfiedBy(parentPath); + } + } else if (boundIndex == 0 && leaf instanceof TypeParameterTree) { + List<? extends Tree> bounds = ((TypeParameterTree) leaf).getBounds(); + if (bounds.isEmpty() || isInterface((JCExpression) bounds.get(0))) { + // If the bound is implicit (i.e., a missing "extends Object"), + // then permit the match here. + returnValue = parentCriterion.isSatisfiedBy(path); + } else { + Type type = ((JCExpression) bounds.get(0)).type; + if (type != null && type.tsym != null && type.tsym.isInterface()) { + returnValue = parentCriterion.isSatisfiedBy(parentPath); + } + } + } + } else if (paramIndex != -1) { + // if paramIndex is not null, need to ensure this present + // typeparameter tree represents the correct parameter + if (parent instanceof MethodTree || parent instanceof ClassTree) { + List<? extends TypeParameterTree> params = null; + + if (parent instanceof MethodTree) { + params = ((MethodTree) parent).getTypeParameters(); + } else if (parent instanceof ClassTree) { + params = ((ClassTree) parent).getTypeParameters(); + } + + if (paramIndex < params.size()) { + if (params.get(paramIndex) == leaf) { + returnValue = true; + } + } + } + } + + if (!returnValue) { + return this.isSatisfiedBy(parentPath); + } else { + return true; + } + } + + private boolean isInterface(JCExpression bound) { + Type type = bound.type; + return type != null && type.tsym != null && type.tsym.isInterface(); + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.BOUND_LOCATION; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "BoundCriterion: at param index: " + paramIndex + + " at bound index: " + boundIndex; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/CallCriterion.java b/annotation-file-utilities/src/annotator/find/CallCriterion.java new file mode 100644 index 0000000..de30898 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/CallCriterion.java
@@ -0,0 +1,60 @@ +package annotator.find; + +import annotations.el.RelativeLocation; +import annotator.scanner.MethodCallScanner; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class CallCriterion implements Criterion { + private final String methodName; + private final RelativeLocation loc; + + public CallCriterion(String methodName, RelativeLocation loc) { + this.methodName = methodName; + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Tree leaf = path.getLeaf(); + + if (leaf.getKind() == Tree.Kind.METHOD_INVOCATION) { + int indexInSource = MethodCallScanner.indexOfMethodCallTree(path, leaf); + boolean b; + if (loc.isBytecodeOffset()) { + int indexInClass = + MethodCallScanner.getMethodCallIndex(methodName, loc.offset); + b = (indexInSource == indexInClass); + } else { + b = (indexInSource == loc.index); + } + return b; + } else { + boolean b = this.isSatisfiedBy(path.getParentPath()); + return b; + } + } + + @Override + public Kind getKind() { + return Kind.METHOD_CALL; + } + + @Override + public String toString() { + return "CallCriterion: in method: " + methodName + " location: " + loc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/CastCriterion.java b/annotation-file-utilities/src/annotator/find/CastCriterion.java new file mode 100644 index 0000000..824e387 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/CastCriterion.java
@@ -0,0 +1,66 @@ +package annotator.find; + +import annotations.el.RelativeLocation; +import annotator.scanner.CastScanner; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * Criterion for being a specific type cast expression. + */ +public class CastCriterion implements Criterion { + + private final String methodName; + private final RelativeLocation loc; + + public CastCriterion(String methodName, RelativeLocation loc) { + this.methodName = methodName.substring(0, methodName.lastIndexOf(")") + 1); + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Tree leaf = path.getLeaf(); + + if (leaf.getKind() == Tree.Kind.TYPE_CAST) { + int indexInSource = CastScanner.indexOfCastTree(path, leaf); + boolean b; + if (loc.isBytecodeOffset()) { + int indexInClass = CastScanner.getMethodCastIndex(methodName, loc.offset); + b = (indexInSource == indexInClass); + } else { + b = (indexInSource == loc.index); + } + return b; + + } else { + boolean b = this.isSatisfiedBy(path.getParentPath()); + return b; + } + } + + public RelativeLocation getLocation() { return loc; } + + @Override + public Kind getKind() { + return Kind.CAST; + } + + @Override + public String toString() { + return "CastCriterion: in method: " + methodName + " location: " + loc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/CastInsertion.java b/annotation-file-utilities/src/annotator/find/CastInsertion.java new file mode 100644 index 0000000..5c74564 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/CastInsertion.java
@@ -0,0 +1,94 @@ +package annotator.find; + +import type.Type; + +/** + * Specifies an insertion of a cast into a source file. Stores the type of cast + * to insert in addition to the annotation and location. + * <p> + * In order to restrict the cast to only the specified expression, a cast + * insertion is of the form: + * + * <pre> + * ((<em>cast type</em>) (<em>original expression</em>)) + * </pre> + * + * This insertion inserts the cast type and parentheses that go before the + * original expression. A {@link CloseParenthesisInsertion} must be used + * after the expression to close the parentheses left open by this insertion. + */ +public class CastInsertion extends Insertion { + + /** + * The type to cast to. + */ + private Type type; + + /** + * Whether insertion is to take place on a bare array literal. + */ + public boolean onArrayLiteral = false; + + /** + * Creates a new CastInsertion. + * + * @param criteria where to insert the text + * @param type the un-annotated type to cast to + */ + public CastInsertion(Criteria criteria, Type type) { + super(criteria, false); + this.type = type; + } + + /** + * Gets the type for this insertion. It is assumed that the returned value will be modified + * to update the type to be inserted. + * @return the type + */ + public Type getType() { + return type; + } + + protected void setType(Type t) { + type = t; + } + + /** {@inheritDoc} */ + @Override + protected String getText(boolean comments, boolean abbreviate) { + String result = onArrayLiteral + ? "((new " + typeToString(type, comments, abbreviate) + " " + : "((" + typeToString(type, comments, abbreviate) + ") ("; + return result; + } + + /** {@inheritDoc} */ + @Override + protected boolean addLeadingSpace(boolean gotSeparateLine, int pos, + char precedingChar) { + // Don't add a leading space if this cast is on the index of an array access. + return super.addLeadingSpace(gotSeparateLine, pos, precedingChar) + && precedingChar != '['; + } + + /** {@inheritDoc} */ + @Override + protected boolean addTrailingSpace(boolean gotSeparateLine) { + // Never add a trailing space after the first part of a cast insertion. + return false; + } + + public boolean isOnArrayLiteral() { + return onArrayLiteral; + } + + public void setOnArrayLiteral(boolean onArrayLiteral) { + this.onArrayLiteral = onArrayLiteral; + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.CAST; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/ClassBoundCriterion.java b/annotation-file-utilities/src/annotator/find/ClassBoundCriterion.java new file mode 100644 index 0000000..77b4f92 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ClassBoundCriterion.java
@@ -0,0 +1,49 @@ +package annotator.find; + +import annotations.el.BoundLocation; + +import com.sun.source.util.TreePath; +import com.sun.source.tree.Tree; + +public class ClassBoundCriterion implements Criterion { + + private final String className; + public final BoundLocation boundLoc; + private final Criterion notInMethodCriterion; + private final Criterion boundLocCriterion; + + public ClassBoundCriterion(String className, BoundLocation boundLoc) { + this.className = className; + this.boundLoc = boundLoc; + this.notInMethodCriterion = Criteria.notInMethod(); + this.boundLocCriterion = Criteria.atBoundLocation(boundLoc); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + return boundLocCriterion.isSatisfiedBy(path) && + notInMethodCriterion.isSatisfiedBy(path); + } + + @Override + public Kind getKind() { + return Kind.CLASS_BOUND; + } + + @Override + public String toString() { + return "ClassBoundCriterion: for " + className + " at " + boundLoc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/CloseParenthesisInsertion.java b/annotation-file-utilities/src/annotator/find/CloseParenthesisInsertion.java new file mode 100644 index 0000000..083afa0 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/CloseParenthesisInsertion.java
@@ -0,0 +1,40 @@ +package annotator.find; + +/** + * This insertion adds two closing parentheses to close the unclosed parentheses + * left by a {@link CastInsertion}. This should be inserted after the expression + * that's being casted. + */ +public class CloseParenthesisInsertion extends Insertion { + + public CloseParenthesisInsertion(Criteria criteria, + boolean separateLine) { + super(criteria, separateLine); + } + + /** {@inheritDoc} */ + @Override + protected String getText(boolean comments, boolean abbreviate) { + return "))"; + } + + /** {@inheritDoc} */ + @Override + protected boolean addLeadingSpace(boolean gotSeparateLine, int pos, + char precedingChar) { + // Never add a leading space when inserting closing parentheses. + return false; + } + + /** {@inheritDoc} */ + @Override + protected boolean addTrailingSpace(boolean gotSeparateLine) { + // Never add a trailing space when inserting closing parentheses. + return false; + } + + /** {@inheritDoc} */ + public Kind getKind() { + return Kind.CLOSE_PARENTHESIS; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/ConstructorInsertion.java b/annotation-file-utilities/src/annotator/find/ConstructorInsertion.java new file mode 100644 index 0000000..e43c61e --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ConstructorInsertion.java
@@ -0,0 +1,102 @@ +package annotator.find; + +import java.util.List; +import java.util.LinkedHashSet; +import java.util.Set; + +import type.Type; + +public class ConstructorInsertion extends TypedInsertion { + private ReceiverInsertion receiverInsertion = null; + private Set<Insertion> declarationInsertions = new LinkedHashSet<Insertion>(); + + /** + * Construct a ConstructorInsertion. + * <p> + * To insert the annotation and the constructor (for example, + * {@code @Anno Type this}) the name should be set to the type to insert. + * This can either be done before calling this constructor, or by modifying + * the return value of {@link #getType()}. + * + * @param type the type to use when inserting the constructor + * @param criteria where to insert the text + * @param innerTypeInsertions the inner types to go on this constructor + */ + public ConstructorInsertion(Type type, Criteria criteria, + List<Insertion> innerTypeInsertions) { + super(type, criteria, true, innerTypeInsertions); + } + + /** {@inheritDoc} */ + @Override + protected String getText(boolean comments, boolean abbreviate) { + StringBuilder b = new StringBuilder(); + if (annotationsOnly) { + // List<String> annotations = type.getAnnotations(); + // if (annotations.isEmpty()) { return ""; } + // for (String a : annotations) { + // b.append(a); + // b.append(' '); + // } + // return new AnnotationInsertion(b.toString(), getCriteria(), + // getSeparateLine()).getText(comments, abbreviate); + return ""; + } else { + boolean commentAnnotation = + comments && getBaseType().getName().isEmpty(); + String typeString = typeToString(type, commentAnnotation, true); + int ix = typeString.lastIndexOf('$'); // FIXME: exclude '$' in source + typeString = typeString.substring(ix+1); + + for (Insertion i : declarationInsertions) { + b.append(i.getText(commentAnnotation, abbreviate)).append("\n"); + if (abbreviate) { + packageNames.addAll(i.getPackageNames()); + } + } + b.append("public ").append(typeString).append("("); + if (receiverInsertion != null && !receiverInsertion.getInserted()) { + b.append(receiverInsertion.getText(comments, abbreviate)); + } + b.append(") { super(); }"); + return b.toString(); + } + } + + protected ReceiverInsertion getReceiverInsertion() { + return receiverInsertion; + } + + public void addReceiverInsertion(ReceiverInsertion recv) { + if (receiverInsertion == null) { + receiverInsertion = recv; + } else { + receiverInsertion.getInnerTypeInsertions() + .addAll(recv.getInnerTypeInsertions()); + } + } + + public void addDeclarationInsertion(Insertion ins) { + declarationInsertions.add(ins); + ins.setInserted(true); + } + + /** {@inheritDoc} */ + @Override + protected boolean addLeadingSpace(boolean gotSeparateLine, int pos, + char precedingChar) { + return false; + } + + /** {@inheritDoc} */ + @Override + protected boolean addTrailingSpace(boolean gotSeparateLine) { + return false; + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.CONSTRUCTOR; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/Criteria.java b/annotation-file-utilities/src/annotator/find/Criteria.java new file mode 100644 index 0000000..c565d29 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/Criteria.java
@@ -0,0 +1,534 @@ +package annotator.find; + +import java.util.LinkedHashMap; +import java.util.Map; + +import annotations.el.BoundLocation; +import annotations.el.InnerTypeLocation; +import annotations.el.LocalLocation; +import annotations.el.RelativeLocation; +import annotations.el.TypeIndexLocation; +import annotations.io.ASTPath; +import annotations.io.DebugWriter; +import annotator.Main; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * Represents a set of Criterion objects for locating a program element in + * a source tree. + * <p> + * + * This class also contains static factory methods for creating a {@code + * Criterion}. + */ +public final class Criteria { + public static DebugWriter dbug = new DebugWriter(); + + /** The set of criterion objects, indexed by kind. */ + private final Map<Criterion.Kind, Criterion> criteria; + + /** + * Creates a new {@code Criteria} without any {@code Criterion}. + */ + public Criteria() { + this.criteria = new LinkedHashMap<Criterion.Kind, Criterion>(); + } + + /** + * Add a {@code Criterion} to this {@code Criteria}. + * + * @param c the criterion to add + */ + public void add(Criterion c) { + criteria.put(c.getKind(), c); + } + + /** + * Determines whether or not the program element at the leaf of the + * specified path is satisfied by these criteria. + * + * @param path the tree path to check against + * @param leaf the tree at the leaf of the path; only relevant when the path + * is null, in which case the leaf is a CompilationUnitTree + * @return true if all of these criteria are satisfied by the given path, + * false otherwise + */ + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + for (Criterion c : criteria.values()) { + if (! c.isSatisfiedBy(path, leaf)) { + dbug.debug("UNsatisfied criterion:%n %s%n %s%n", + c, Main.pathToString(path)); + return false; + } else { + dbug.debug("satisfied criterion:%n %s%n %s%n", + c, Main.pathToString(path)); + } + } + return true; + } + + /** + * Determines whether or not the program element at the leaf of the + * specified path is satisfied by these criteria. + * + * @param path the tree path to check against + * @return true if all of these criteria are satisfied by the given path, + * false otherwise + */ + public boolean isSatisfiedBy(TreePath path) { + for (Criterion c : criteria.values()) { + if (! c.isSatisfiedBy(path)) { + dbug.debug("UNsatisfied criterion: %s%n", c); + return false; + } else { + dbug.debug("satisfied criterion: %s%n", c); + } + } + return true; + } + + /** + * Determines whether this is the criteria on a receiver. + * + * @return true iff this is the criteria on a receiver + */ + public boolean isOnReceiver() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.RECEIVER) { + return true; + } + } + + return false; + } + + /** + * Determines whether this is the criteria on a package. + * + * @return true iff this is the criteria on a package + */ + public boolean isOnPackage() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.PACKAGE) { + return true; + } + } + + return false; + } + + /** + * Determines whether this is the criteria on a return type. + * + * @return true iff this is the criteria on a return type + */ + public boolean isOnReturnType() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.RETURN_TYPE) { + return true; + } + } + + return false; + } + + /** + * Determines whether this is the criteria on a local variable. + * + * @return true iff this is the criteria on a local variable + */ + public boolean isOnLocalVariable() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.LOCAL_VARIABLE) { + return true; + } + } + + return false; + } + + /** + * Determines whether this is the criteria on the RHS of an occurrence + * of 'instanceof'. + */ + public boolean isOnInstanceof() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.INSTANCE_OF) { + return true; + } + } + return false; + } + + /** + * Determines whether this is the criteria on an object initializer. + */ + public boolean isOnNew() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.NEW) { + return true; + } + } + return false; + } + + /** + * Determines whether this is the criteria on a class {@code extends} bound. + */ + public boolean isOnTypeDeclarationExtendsClause() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.EXTIMPLS_LOCATION) { + return ((ExtImplsLocationCriterion) c).getIndex() == -1; + } + } + return false; + } + + /** + * Returns true if this Criteria is on the given method. + */ + public boolean isOnMethod(String methodname) { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.IN_METHOD) { + if (((InMethodCriterion) c).name.equals(methodname)) { + return true; + } + } + } + return false; + } + + /** + * Returns true if this Criteria is on the given method. + */ + public boolean isOnFieldDeclaration() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.FIELD + && ((FieldCriterion) c).isDeclaration) { + return true; + } + } + return false; + } + + /** + * Gives the AST path specified in the criteria, if any. + * + * @return AST path from {@link ASTPathCriterion}, or null if none present + */ + public ASTPath getASTPath() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.AST_PATH) { + return ((ASTPathCriterion) c).astPath; + } + } + + return null; + } + + /** + * Returns the name of the class specified in the Criteria, if any. + * + * @return class name from {@link InClassCriterion}, or null if none present + */ + public String getClassName() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.IN_CLASS) { + return ((InClassCriterion) c).className; + } + } + + return null; + } + + /** + * Returns the name of the method specified in the Criteria, if any. + * + * @return method name from {@link InMethodCriterion}, or null if none present + */ + public String getMethodName() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.IN_METHOD) { + return ((InMethodCriterion) c).name; + } + } + + return null; + } + + /** + * Returns the name of the member field specified in the Criteria, if any. + * + * @return field name from {@link FieldCriterion}, or null if none present + */ + public String getFieldName() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.FIELD) { + return ((FieldCriterion) c).varName; + } + } + + return null; + } + + /** + * @return a GenericArrayLocationCriterion if this has one, else null + */ + public GenericArrayLocationCriterion getGenericArrayLocation() { + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.GENERIC_ARRAY_LOCATION) { + return (GenericArrayLocationCriterion) c; + } + } + return null; + } + + /** + * @return a RelativeCriterion if this has one, else null + */ + public RelativeLocation getCastRelativeLocation() { + RelativeLocation result = null; + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.CAST) { + result = ((CastCriterion) c).getLocation(); + } + } + return result; + } + + // Returns the last one. Should really return the outermost one. + // However, there should not be more than one unless all are equivalent. + /** + * @return an InClassCriterion if this has one, else null + */ + public InClassCriterion getInClass() { + InClassCriterion result = null; + for (Criterion c : criteria.values()) { + if (c.getKind() == Criterion.Kind.IN_CLASS) { + result = (InClassCriterion) c; + } + } + return result; + } + + /** + * @return true if this is on the zeroth bound of a type + */ + // Used when determining whether an annotation is on an implicit upper + // bound (the "extends Object" that is customarily omitted). + public boolean onBoundZero() { + for (Criterion c : criteria.values()) { + switch (c.getKind()) { + case CLASS_BOUND: + if (((ClassBoundCriterion) c).boundLoc.boundIndex != 0) { break; } + return true; + case METHOD_BOUND: + if (((MethodBoundCriterion) c).boundLoc.boundIndex != 0) { break; } + return true; + case AST_PATH: + ASTPath astPath = ((ASTPathCriterion) c).astPath; + if (!astPath.isEmpty()) { + ASTPath.ASTEntry entry = astPath.get(-1); + if (entry.childSelectorIs(ASTPath.BOUND) + && entry.getArgument() == 0) { + return true; + } + } + break; + default: + break; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return criteria.toString(); + } + + + /////////////////////////////////////////////////////////////////////////// + /// Factory methods + /// + + /** + * Creates an "is" criterion: that a program element has the specified + * kind and name. + * + * @param kind the program element's kind + * @param name the program element's name + * @return an "is" criterion + */ + public final static Criterion is(Tree.Kind kind, String name) { + return new IsCriterion(kind, name); + } + + /** + * Creates an "enclosed by" criterion: that a program element is enclosed + * by the specified kind of program element. + * + * @param kind the kind of enclosing program element + * @return an "enclosed by" criterion + */ + public final static Criterion enclosedBy(Tree.Kind kind) { + return new EnclosedByCriterion(kind); + } + + /** + * Creates an "in package" criterion: that a program element is enclosed + * by the specified package. + * + * @param name the name of the enclosing package + * @return an "in package" criterion + */ + public final static Criterion inPackage(String name) { + return new InPackageCriterion(name); + } + + /** + * Creates an "in class" criterion: that a program element is enclosed + * by the specified class. + * + * @param name the name of the enclosing class + * @param exact whether to match only in the class itself, not in its inner classes + * @return an "in class" criterion + */ + public final static Criterion inClass(String name, boolean exact) { + return new InClassCriterion(name, /*exactmatch=*/ true); + } + + /** + * Creates an "in method" criterion: that a program element is enclosed + * by the specified method. + * + * @param name the name of the enclosing method + * @return an "in method" criterion + */ + public final static Criterion inMethod(String name) { + return new InMethodCriterion(name); + } + + /** + * Creates a "not in method" criterion: that a program element is not + * enclosed by any method. + * + * @return a "not in method" criterion + */ + public final static Criterion notInMethod() { + return new NotInMethodCriterion(); + } + + public final static Criterion packageDecl(String packageName) { + return new PackageCriterion(packageName); + } + + public final static Criterion atLocation() { + return new GenericArrayLocationCriterion(); + } + + public final static Criterion atLocation(InnerTypeLocation loc) { + return new GenericArrayLocationCriterion(loc); + } + + @Deprecated + public final static Criterion field(String varName) { + return new FieldCriterion(varName); + } + + public final static Criterion field(String varName, boolean isOnDeclaration) { + return new FieldCriterion(varName, isOnDeclaration); + } + + public final static Criterion inStaticInit(int blockID) { + return new InInitBlockCriterion(blockID, true); + } + + public final static Criterion inInstanceInit(int blockID) { + return new InInitBlockCriterion(blockID, false); + } + + public final static Criterion inFieldInit(String varName) { + return new InFieldInitCriterion(varName); + } + + public final static Criterion receiver(String methodName) { + return new ReceiverCriterion(methodName); + } + + public final static Criterion returnType(String className, String methodName) { + return new ReturnTypeCriterion(className, methodName); + } + + public final static Criterion isSigMethod(String methodName) { + return new IsSigMethodCriterion(methodName); + } + + + public final static Criterion param(String methodName, Integer pos) { + return new ParamCriterion(methodName, pos); + } +// +// public final static Criterion param(String methodName, Integer pos, InnerTypeLocation loc) { +// return new ParamCriterion(methodName, pos, loc); +// } + + + public final static Criterion local(String methodName, LocalLocation loc) { + return new LocalVariableCriterion(methodName, loc); + } + + public final static Criterion cast(String methodName, RelativeLocation loc) { + return new CastCriterion(methodName, loc); + } + + public final static Criterion newObject(String methodName, RelativeLocation loc) { + return new NewCriterion(methodName, loc); + } + + public final static Criterion instanceOf(String methodName, RelativeLocation loc) { + return new InstanceOfCriterion(methodName, loc); + } + + public static Criterion memberReference(String methodName, RelativeLocation loc) { + return new MemberReferenceCriterion(methodName, loc); + } + + public static Criterion methodCall(String methodName, RelativeLocation loc) { + return new CallCriterion(methodName, loc); + } + + public final static Criterion typeArgument(String methodName, RelativeLocation loc) { + return new TypeArgumentCriterion(methodName, loc); + } + + public final static Criterion lambda(String methodName, RelativeLocation loc) { + return new LambdaCriterion(methodName, loc); + } + + public final static Criterion atBoundLocation(BoundLocation loc) { + return new BoundLocationCriterion(loc); + } + + public final static Criterion atExtImplsLocation(String className, TypeIndexLocation loc) { + return new ExtImplsLocationCriterion(className, loc); + } + + public final static Criterion methodBound(String methodName, BoundLocation boundLoc) { + return new MethodBoundCriterion(methodName, boundLoc); + } + + public final static Criterion classBound(String className, BoundLocation boundLoc) { + return new ClassBoundCriterion(className, boundLoc); + } + + public final static Criterion astPath(ASTPath astPath) { + return new ASTPathCriterion(astPath); + } +}
diff --git a/annotation-file-utilities/src/annotator/find/Criterion.java b/annotation-file-utilities/src/annotator/find/Criterion.java new file mode 100644 index 0000000..1b874c3 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/Criterion.java
@@ -0,0 +1,86 @@ +package annotator.find; + +import com.sun.source.util.TreePath; +import com.sun.source.tree.Tree; + +/** + * A criterion for locating a program element in an AST. A Criterion does + * not actually give a location. Given a location, the isSatisfiedBy + * method indicates whether that location is a desired one. + */ +public interface Criterion { + + /** + * Types of criterion. + */ + public static enum Kind { + IN_METHOD, + /* + * Used for classes, interfaces, enums, annotation types. + * What would be a better name? + * Also see Criteria.isClassEquiv + */ + IN_CLASS, + ENCLOSED_BY, + HAS_KIND, + NOT_IN_METHOD, + TYPE_PARAM, + GENERIC_ARRAY_LOCATION, + RECEIVER, + RETURN_TYPE, + SIG_METHOD, + PARAM, + CAST, + LOCAL_VARIABLE, + FIELD, + NEW, + INSTANCE_OF, + TYPE_ARGUMENT, + METHOD_CALL, + METHOD_REFERENCE, + LAMBDA_EXPRESSION, + BOUND_LOCATION, + EXTIMPLS_LOCATION, + INTERSECT_LOCATION, + METHOD_BOUND, + CLASS_BOUND, + IN_PACKAGE, + AST_PATH, + IN_STATIC_INIT, + IN_INSTANCE_INIT, + IN_FIELD_INIT, + /* + * This constant is never used. What is the difference to IN_CLASS? + * Is one for anywhere within a class and this one only for the + * class declaration itself? + */ + CLASS, + PACKAGE; + } + + /** + * Determines if the given tree path is satisfied by this criterion. + * + * @param path the tree path to check against + * @return true if this criterion is satisfied by the given path, + * false otherwise + */ + public boolean isSatisfiedBy(TreePath path, Tree tree); + + /** + * Determines if the given tree path is satisfied by this criterion. + * + * @param path the tree path to check against + * @return true if this criterion is satisfied by the given path, + * false otherwise + */ + public boolean isSatisfiedBy(TreePath path); + + /** + * Gets the type of this criterion. + * + * @return this criterion's kind + */ + public Kind getKind(); + +}
diff --git a/annotation-file-utilities/src/annotator/find/EnclosedByCriterion.java b/annotation-file-utilities/src/annotator/find/EnclosedByCriterion.java new file mode 100644 index 0000000..a160187 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/EnclosedByCriterion.java
@@ -0,0 +1,54 @@ +package annotator.find; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * Represents the criterion that a program element is enclosed (directly or + * indirect) by a program element of a certain type. + */ +final class EnclosedByCriterion implements Criterion { + + private final Tree.Kind kind; + + EnclosedByCriterion(Tree.Kind kind) { + this.kind = kind; + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.ENCLOSED_BY; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + + if (path == null) { + return false; + } + + for (Tree tree : path) { + if (tree.getKind() == kind) { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "enclosed by '" + kind + "'"; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/ExtImplsLocationCriterion.java b/annotation-file-utilities/src/annotator/find/ExtImplsLocationCriterion.java new file mode 100644 index 0000000..c985039 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ExtImplsLocationCriterion.java
@@ -0,0 +1,103 @@ +package annotator.find; + +import java.util.List; + +import annotations.el.TypeIndexLocation; +import annotator.scanner.CommonScanner; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.tree.JCTree; + +/** + * A criterion to find a given extends or implements clause. + */ +public class ExtImplsLocationCriterion implements Criterion { + + private final String classname; + private final Integer index; + + /** + * @param classname the class name; for debugging purposes only, not used to constrain + * @param tyLoc -1 for an extends clause, $ge; 0 for the zero-based implements clause + */ + public ExtImplsLocationCriterion(String classname, TypeIndexLocation tyLoc) { + this.classname = classname; + this.index = tyLoc.typeIndex; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Tree leaf = path.getLeaf(); + + // System.out.printf("ExtImplsLocationCriterion.isSatisfiedBy(%s):%n leaf=%s (%s)%n", path, leaf, leaf.getClass()); + + TreePath parentPath = path.getParentPath(); + if (parentPath == null) { + return false; + } + + Tree parent = parentPath.getLeaf(); + if (parent == null) { + return false; + } + + // System.out.printf("ExtImplsLocationCriterion.isSatisfiedBy(%s):%n leaf=%s (%s)%n parent=%s (%s)%n", path, leaf, leaf.getClass(), parent, parent.getClass()); + + boolean returnValue = false; + + if (index == -1 && leaf.getKind() == Tree.Kind.CLASS) { + return ((JCTree.JCClassDecl) leaf).getExtendsClause() == null; + } + if (CommonScanner.hasClassKind(parent)) { + ClassTree ct = (ClassTree) parent; + + if (index==-1) { + Tree ext = ct.getExtendsClause(); + if (ext == leaf) { + returnValue = true; + } + } else { + List<? extends Tree> impls = ct.getImplementsClause(); + if (index < impls.size() && impls.get(index) == leaf) { + returnValue = true; + } + } + } + + if (!returnValue) { + return this.isSatisfiedBy(parentPath); + } else { + return true; + } + } + + public Integer getIndex() { + return index; + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.EXTIMPLS_LOCATION; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return "ExtImplsLocationCriterion: class " + classname + " at type index: " + index; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/FieldCriterion.java b/annotation-file-utilities/src/annotator/find/FieldCriterion.java new file mode 100644 index 0000000..a4fec51 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/FieldCriterion.java
@@ -0,0 +1,56 @@ +package annotator.find; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class FieldCriterion implements Criterion { + + public final String varName; + public final boolean isDeclaration; + public final Criterion varCriterion; + public final Criterion notInMethodCriterion; + + public FieldCriterion(String varName) { + this(varName, false); + } + + public FieldCriterion(String varName, boolean isDeclaration) { + this.varName = varName; + this.isDeclaration = isDeclaration; + this.varCriterion = Criteria.is(Tree.Kind.VARIABLE, varName); + this.notInMethodCriterion = Criteria.notInMethod(); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null || (isDeclaration + && path.getLeaf().getKind() != Tree.Kind.VARIABLE)) { + return false; + } + + if (varCriterion.isSatisfiedBy(path) && + notInMethodCriterion.isSatisfiedBy(path)) { + return true; + } else { + return this.isSatisfiedBy(path.getParentPath()); + } + } + + @Override + public Kind getKind() { + return Kind.FIELD; + } + + @Override + public String toString() { + return "FieldCriterion: " + varName; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/GenericArrayLocationCriterion.java b/annotation-file-utilities/src/annotator/find/GenericArrayLocationCriterion.java new file mode 100644 index 0000000..4595df2 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/GenericArrayLocationCriterion.java
@@ -0,0 +1,512 @@ +package annotator.find; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.lang.model.type.TypeKind; + +import annotations.el.InnerTypeLocation; +import annotator.Main; + +import com.sun.source.tree.AnnotatedTypeTree; +import com.sun.source.tree.ArrayTypeTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.ParameterizedTypeTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import com.sun.source.tree.WildcardTree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; + +/** + * GenericArrayLocationCriterion represents the criterion specifying the location + * of an element in the generic/array hierarchy as specified by the + * JSR 308 proposal. + */ +public class GenericArrayLocationCriterion implements Criterion { + private static final boolean debug = false; + + // the full location list + private final List<TypePathEntry> location; + + // represents all but the last element of the location list + // TODO: this field is initialized, but never read! + // I removed it, see the version control history. + // private Criterion parentCriterion; + + /** + * Creates a new GenericArrayLocationCriterion specifying that the element + * is an outer type, such as: + * <code>@A List<Integer></code> + * or + * <code>Integer @A []</code> + */ + public GenericArrayLocationCriterion() { + this(new ArrayList<TypePathEntry>()); + } + + /** + * Creates a new GenericArrayLocationCriterion representing the given + * location. + * + * @param innerTypeLoc the location of the element being represented + */ + public GenericArrayLocationCriterion(InnerTypeLocation innerTypeLoc) { + this(innerTypeLoc.location); + } + + private GenericArrayLocationCriterion(List<TypePathEntry> location) { + this.location = location; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** + * Determines if the given list holds only {@link TypePathEntry}s with the tag + * {@link TypePathEntryKind#ARRAY}. + * + * @param location the list to check + * @return {@code true} if the list only contains + * {@link TypePathEntryKind#ARRAY}, {@code false} otherwise. + */ + private boolean containsOnlyArray(List<TypePathEntry> location) { + for (TypePathEntry tpe : location) { + if (tpe.tag != TypePathEntryKind.ARRAY) { + return false; + } + } + return true; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null || path.getParentPath() == null) { + if (debug) { + System.out.println("GenericArrayLocationCriterion.isSatisfiedBy() with null path gives false."); + } + return false; + } + + if (debug) { + System.out.printf("GenericArrayLocationCriterion.isSatisfiedBy():%n leaf of path: %s%n searched location: %s%n", + path.getLeaf(), location); + } + + TreePath pathRemaining = path; + Tree leaf = path.getLeaf(); + + // Don't allow annotations directly on these tree kinds if the child type is + // a MEMBER_SELECT. This way we'll continue to traverse deeper in the tree + // to find the correct MEMBER_SELECT. + Tree child = null; + if (leaf.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + child = ((ParameterizedTypeTree) leaf).getType(); + } else if (leaf.getKind() == Tree.Kind.VARIABLE) { + child = ((VariableTree) leaf).getType(); + } else if (leaf.getKind() == Tree.Kind.NEW_CLASS) { + child = ((NewClassTree) leaf).getIdentifier(); + } else if (leaf.getKind() == Tree.Kind.NEW_ARRAY && !location.isEmpty()) { + child = ((NewArrayTree) leaf).getType(); + } + if (child != null && child.getKind() == Tree.Kind.MEMBER_SELECT) { + JCExpression exp = ((JCFieldAccess) child).getExpression(); + if (exp.type != null && exp.type.getKind() == TypeKind.PACKAGE + || location.isEmpty() + || (location.get(location.size()-1)).tag + != TypePathEntryKind.INNER_TYPE) { + return false; + } + } + + if (leaf.getKind() == Tree.Kind.MEMBER_SELECT) { + JCFieldAccess fieldAccess = (JCFieldAccess) leaf; + if (isStatic(fieldAccess)) { + // If this MEMBER_SELECT is for a static class... + if (location.isEmpty()) { + // ...and it does not go on a compound type, this is the right place. + return true; + } else if (isGenericOrArray(path.getParentPath().getLeaf()) + && isGenericOrArray(path.getParentPath().getParentPath().getLeaf())) { + // If the two parents above this are compound types, then skip + // the compound type directly above. For example, to get to Outer.Inner + // of Outer.Inner<K, V> we had to get through the PARAMETERIZED_TYPE + // node. But we didn't go deeper in the compound type in the way the + // type path defines, we just went deeper to get to the outer type. So + // skip this node later when checking to make sure that we're in the + // correct part of the compound type. + pathRemaining = path.getParentPath(); + } + } else { + JCExpression exp = fieldAccess.getExpression(); + if (exp.getKind() == Tree.Kind.MEMBER_SELECT && exp.type != null + && exp.type.getKind() == TypeKind.PACKAGE) { + if (location.isEmpty()) { + return true; + } // else, keep going to make sure we're in the right part of the + // compound type + } else { + if (!location.isEmpty() + && location.get(location.size()-1).tag + != TypePathEntryKind.INNER_TYPE) { + return false; + } + } + } + } + + if (location.isEmpty()) { + // no inner type location, want to annotate outermost type + // e.g., @Nullable List list; + // @Nullable List<String> list; + // String @Nullable [] array; + leaf = path.getLeaf(); + Tree parent = path.getParentPath().getLeaf(); + + boolean result = ((leaf.getKind() == Tree.Kind.NEW_ARRAY) + || (leaf.getKind() == Tree.Kind.NEW_CLASS) + || (leaf.getKind() == Tree.Kind.ANNOTATED_TYPE + && isSatisfiedBy(TreePath.getPath(path, + ((AnnotatedTypeTree) leaf).getUnderlyingType()))) + || ((isGenericOrArray(leaf) + // or, it might be a raw type + || (leaf.getKind() == Tree.Kind.IDENTIFIER) // IdentifierTree + || (leaf.getKind() == Tree.Kind.METHOD) // MethodTree + || (leaf.getKind() == Tree.Kind.TYPE_PARAMETER) // TypeParameterTree + // I don't know why a GenericArrayLocationCriterion + // is being created in this case, but it is. + || (leaf.getKind() == Tree.Kind.PRIMITIVE_TYPE) // PrimitiveTypeTree + // TODO: do we need wildcards here? + // || leaf instanceof WildcardTree + ) + && ! isGenericOrArray(parent))); + if (debug) { + System.out.printf("GenericArrayLocationCriterion.isSatisfiedBy: locationInParent==null%n leaf=%s (%s)%n parent=%s (%s)%n => %s (%s %s)%n", + leaf, leaf.getClass(), parent, parent.getClass(), result, isGenericOrArray(leaf), ! isGenericOrArray(parent)); + } + + return result; + } + + // If we've made it this far then we've determined that *if* this is the right + // place to insert the annotation this is the MEMBER_SELECT it should be + // inserted on. So remove the rest of the MEMBER_SELECTs to get down to the + // compound type and make sure the compound type location matches. + while (pathRemaining.getParentPath().getLeaf().getKind() == Tree.Kind.MEMBER_SELECT) { + pathRemaining = pathRemaining.getParentPath(); + } + + List<TypePathEntry> locationRemaining = new ArrayList<TypePathEntry>(location); + + while (locationRemaining.size() != 0) { + // annotating an inner type + leaf = pathRemaining.getLeaf(); + if ((leaf.getKind() == Tree.Kind.NEW_ARRAY) + && containsOnlyArray(locationRemaining)) { + if (debug) { + System.out.println("Found a matching NEW_ARRAY"); + } + return true; + } + TreePath parentPath = pathRemaining.getParentPath(); + if (parentPath == null) { + if (debug) { + System.out.println("Parent path is null and therefore false."); + } + return false; + } + Tree parent = parentPath.getLeaf(); + + if (parent.getKind() == Tree.Kind.ANNOTATED_TYPE) { + // If the parent is an annotated type, we did not really go up a level. + // Therefore, skip up one more level. + parentPath = parentPath.getParentPath(); + parent = parentPath.getLeaf(); + } + + if (debug) { + System.out.printf("locationRemaining: %s, leaf: %s parent: %s %s%n", + locationRemaining, Main.treeToString(leaf), Main.treeToString(parent), parent.getClass()); + } + + TypePathEntry loc = locationRemaining.get(locationRemaining.size()-1); + if (loc.tag == TypePathEntryKind.INNER_TYPE) { + if (leaf.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + leaf = parent; + parentPath = parentPath.getParentPath(); + parent = parentPath.getLeaf(); + } + if (leaf.getKind() != Tree.Kind.MEMBER_SELECT) { return false; } + + JCFieldAccess fieldAccess = (JCFieldAccess) leaf; + if (isStatic(fieldAccess)) { + return false; + } + locationRemaining.remove(locationRemaining.size()-1); + leaf = fieldAccess.selected; + pathRemaining = parentPath; + // TreePath.getPath(pathRemaining.getCompilationUnit(), leaf); + } else if (loc.tag == TypePathEntryKind.WILDCARD + && leaf.getKind() == Tree.Kind.UNBOUNDED_WILDCARD) { + // Check if the leaf is an unbounded wildcard instead of the parent, since unbounded + // wildcard has no members so it can't be the parent of anything. + if (locationRemaining.size() == 0) { + return false; + } + + // The following check is necessary because Oracle has decided that + // x instanceof Class<? extends Object> + // will remain illegal even though it means the same thing as + // x instanceof Class<?>. + TreePath gpath = parentPath.getParentPath(); + if (gpath != null) { // TODO: skip over existing annotations? + Tree gparent = gpath.getLeaf(); + if (gparent.getKind() == Tree.Kind.INSTANCE_OF) { + TreeFinder.warn.debug("WARNING: wildcard bounds not allowed " + + "in 'instanceof' expression; skipping insertion%n"); + return false; + } else if (gparent.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + gpath = gpath.getParentPath(); + if (gpath != null + && gpath.getLeaf().getKind() == Tree.Kind.ARRAY_TYPE) { + TreeFinder.warn.debug("WARNING: wildcard bounds not allowed " + + "in generic array type; skipping insertion%n"); + return false; + } + } + } + locationRemaining.remove(locationRemaining.size() - 1); + } else if (parent.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + if (loc.tag != TypePathEntryKind.TYPE_ARGUMENT) { + return false; + } + + // Find the outermost type in the AST; if it has parameters, + // pop the stack once for each inner type on the end of the type + // path and check the parameter. + Tree inner = ((ParameterizedTypeTree) parent).getType(); + int i = locationRemaining.size() - 1; // last valid type path index + locationRemaining.remove(i--); + while (inner.getKind() == Tree.Kind.MEMBER_SELECT + && !isStatic((JCFieldAccess) inner)) { + // fieldAccess.type != null && fieldAccess.type.getKind() == TypeKind.DECLARED + // && fieldAccess.type.tsym.isStatic() + // TODO: check whether MEMBER_SELECT indicates inner or qualifier? + if (i < 0) { break; } + if (locationRemaining.get(i).tag != TypePathEntryKind.INNER_TYPE) { + return false; + } + locationRemaining.remove(i--); + inner = ((MemberSelectTree) inner).getExpression(); + if (inner.getKind() == Tree.Kind.ANNOTATED_TYPE) { + inner = ((AnnotatedTypeTree) inner).getUnderlyingType(); + } + if (inner.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + inner = ((ParameterizedTypeTree) inner).getType(); + } + } + if (i >= 0 && locationRemaining.get(i).tag == + TypePathEntryKind.INNER_TYPE) { + return false; + } + + // annotating List<@A Integer> + // System.out.printf("parent instanceof ParameterizedTypeTree: %s loc=%d%n", + // Main.treeToString(parent), loc); + List<? extends Tree> childTrees = + ((ParameterizedTypeTree) parent).getTypeArguments(); + boolean found = false; + if (childTrees.size() > loc.arg) { + Tree childi = childTrees.get(loc.arg); + if (childi.getKind() == Tree.Kind.ANNOTATED_TYPE) { + childi = ((AnnotatedTypeTree) childi).getUnderlyingType(); + } + if (childi == leaf) { + for (TreePath outerPath = parentPath.getParentPath(); + outerPath.getLeaf().getKind() == Tree.Kind.MEMBER_SELECT + && !isStatic((JCFieldAccess) outerPath.getLeaf()); + outerPath = outerPath.getParentPath()) { + outerPath = outerPath.getParentPath(); + if (outerPath.getLeaf().getKind() == Tree.Kind.ANNOTATED_TYPE) { + outerPath = outerPath.getParentPath(); + } + if (outerPath.getLeaf().getKind() != Tree.Kind.PARAMETERIZED_TYPE) { + break; + } + parentPath = outerPath; + } + pathRemaining = parentPath; + found = true; + } + } + if (!found) { + if (debug) { + System.out.printf("Generic failed for leaf: %s: nr children: %d loc: %s child: %s%n", + leaf, childTrees.size(), loc, + ((childTrees.size() > loc.arg) ? childTrees.get(loc.arg) : null)); + } + return false; + } + } else if (parent.getKind() == Tree.Kind.EXTENDS_WILDCARD + || parent.getKind() == Tree.Kind.SUPER_WILDCARD) { + if (loc.tag != TypePathEntryKind.WILDCARD || locationRemaining.size() == 1) { + // If there's only one location left, this can't be a match since a wildcard + // needs to be in another kind of compound type. + return false; + } + locationRemaining.remove(locationRemaining.size() - 1); + // annotating List<? extends @A Integer> + // System.out.printf("parent instanceof extends WildcardTree: %s loc=%d%n", + // Main.treeToString(parent), loc); + WildcardTree wct = (WildcardTree) parent; + Tree boundTree = wct.getBound(); + + if (debug) { + String wildcardType; + if (parent.getKind() == Tree.Kind.EXTENDS_WILDCARD) { + wildcardType = "ExtendsWildcard"; + } else { + wildcardType = "SuperWildcard"; + } + System.out.printf("%s with bound %s gives %s%n", wildcardType, boundTree, boundTree.equals(leaf)); + } + + if (boundTree.equals(leaf)) { + if (locationRemaining.isEmpty()) { + return true; + } else { + pathRemaining = parentPath; + } + } else { + return false; + } + } else if (parent.getKind() == Tree.Kind.ARRAY_TYPE) { + if (loc.tag != TypePathEntryKind.ARRAY) { + return false; + } + locationRemaining.remove(locationRemaining.size() - 1); + // annotating Integer @A [] + parentPath = TreeFinder.largestContainingArray(parentPath); + parent = parentPath.getLeaf(); + // System.out.printf("parent instanceof ArrayTypeTree: %s loc=%d%n", + // parent, loc); + Tree elt = ((ArrayTypeTree) parent).getType(); + while (locationRemaining.size() > 0 + && locationRemaining.get(locationRemaining.size() - 1).tag == TypePathEntryKind.ARRAY) { + if (elt.getKind() != Tree.Kind.ARRAY_TYPE) { // ArrayTypeTree + if (debug) { + System.out.printf("Element: %s is not an ArrayTypeTree and therefore false.\n", elt); + } + return false; + } + elt = ((ArrayTypeTree) elt).getType(); + locationRemaining.remove(locationRemaining.size() - 1); + } + + boolean b = elt.equals(leaf); + if (debug) { + System.out.printf("parent %s instanceof ArrayTypeTree: %b %s %s %s%n", + parent, elt.equals(leaf), elt, leaf, loc); + System.out.printf("b=%s elt=%s leaf=%s%n", b, elt, leaf); + } + + // TODO: The parent criterion should be exact, not just "in". + // Otherwise the criterion [1] matches [5 4 3 2 1]. + // This is a disadvantage of working from the inside out instead of the outside in. + if (b) { + pathRemaining = parentPath; + } else { + return false; + } + } else if (parent.getKind() == Tree.Kind.NEW_ARRAY) { + if (loc.tag != TypePathEntryKind.ARRAY) { + return false; + } + if (debug) { + System.out.println("Parent is a NEW_ARRAY and always gives true."); + } + return true; + } else { + if (debug) { + System.out.printf("unrecognized parent kind = %s%n", parent.getKind()); + } + return false; + } + } + + // no (remaining) inner type location, want to annotate outermost type + // e.g., @Nullable List list; + // @Nullable List<String> list; + TreePath parentPath = pathRemaining.getParentPath(); + if (parentPath == null) { + if (debug) { + System.out.println("Parent path is null and therefore false."); + } + return false; + } + Tree parent = pathRemaining.getParentPath().getLeaf(); + if (debug) { + leaf = pathRemaining.getLeaf(); + System.out.printf("No (remaining) inner type location:%n leaf: %s %b%n parent: %s %b%n result: %s%n", + Main.treeToString(leaf), isGenericOrArray(leaf), + Main.treeToString(parent), isGenericOrArray(parent), + ! isGenericOrArray(parent)); + } + + return ! isGenericOrArray(parent); + } + + /** + * @param fieldAccess + * @return + */ + private boolean isStatic(JCFieldAccess fieldAccess) { + return fieldAccess.type != null + && fieldAccess.type.getKind() == TypeKind.DECLARED + && fieldAccess.type.tsym.isStatic(); + } + + private boolean isGenericOrArray(Tree t) { + return ((t.getKind() == Tree.Kind.PARAMETERIZED_TYPE) + || (t.getKind() == Tree.Kind.ARRAY_TYPE) + || (t.getKind() == Tree.Kind.EXTENDS_WILDCARD) + || (t.getKind() == Tree.Kind.SUPER_WILDCARD) + || (t.getKind() == Tree.Kind.ANNOTATED_TYPE && + isGenericOrArray(((AnnotatedTypeTree)t).getUnderlyingType())) + // Monolithic: one node for entire "new". So, handle specially. + // || (t.getKind() == Tree.Kind.NEW_ARRAY) + ); + } + + @Override + public Kind getKind() { + return Criterion.Kind.GENERIC_ARRAY_LOCATION; + } + + @Override + public String toString() { + return "GenericArrayLocationCriterion at " + + ((location.isEmpty()) + ? "outermost type" + : ("( " + location.toString() + " )")); + } + + /** + * Gets the type path location of this criterion. + * + * @return an unmodifiable list of {@link TypePathEntry}s + */ + public List<TypePathEntry> getLocation() { + return Collections.unmodifiableList(location); + } +}
diff --git a/annotation-file-utilities/src/annotator/find/InClassCriterion.java b/annotation-file-utilities/src/annotator/find/InClassCriterion.java new file mode 100644 index 0000000..ee418bf --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/InClassCriterion.java
@@ -0,0 +1,280 @@ +package annotator.find; + +import java.util.*; +import java.util.regex.*; + +import javax.lang.model.element.Name; + +import annotator.scanner.AnonymousClassScanner; +import annotator.scanner.LocalClassScanner; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +// If there are dollar signs in a name, then there are two +// possibilities regarding how the dollar sign got there. +// 1. Inserted by the compiler, for inner classes. +// 2. Written by the programmer (or by a tool that creates .class files). +// We need to account for both possibilities (and all combinations of them). + +// Example names +// annotator.tests.FullClassName +// annotator.tests.FullClassName$InnerClass +// annotator.tests.FullClassName$0 + +/** + * Represents the criterion that a program element is in a class with a + * particular name. + */ +public final class InClassCriterion implements Criterion { + + static boolean debug = false; + + public final String className; + private final boolean exactMatch; + + /** The argument is a fully-qualified class name. */ + public InClassCriterion(String className, boolean exactMatch) { + this.className = className; + this.exactMatch = exactMatch; + } + + /** + * {@inheritDoc} + */ + @Override + public Kind getKind() { + return Kind.IN_CLASS; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + return InClassCriterion.isSatisfiedBy(path, className, exactMatch); + } + + static Pattern anonclassPattern; + static Pattern localClassPattern; + static { + // for JDK 7: anonclassPattern = Pattern.compile("^(?<num>[0-9]+)(\\$(?<remaining>.*))?$"); + anonclassPattern = Pattern.compile("^([0-9]+)(\\$(.*))?$"); + localClassPattern = Pattern.compile("^([0-9]+)([^$]+)(\\$(.*))?$"); + } + + public static boolean isSatisfiedBy(TreePath path, String className, boolean exactMatch) { + if (path == null) { + return false; + } + + // However much of the class name remains to match. + String cname = className; + + // It is wrong to work from the leaf up to the root of the tree, which + // would fail if the criterion is a.b.c and the actual is a.b.c.c. + List<Tree> trees = new ArrayList<Tree>(); + for (Tree tree : path) { + trees.add(tree); + } + Collections.reverse(trees); + + boolean insideMatch = false; + for (int i = 0; i < trees.size(); i++) { + Tree tree = trees.get(i); + boolean checkAnon = false; + boolean checkLocal = false; + + switch (tree.getKind()) { + case COMPILATION_UNIT: + debug("InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + ExpressionTree packageTree = ((CompilationUnitTree) tree).getPackageName(); + if (packageTree == null) { + // compilation unit is in default package; nothing to do + } else { + String declaredPackage = packageTree.toString(); + if (cname.startsWith(declaredPackage + ".")) { + cname = cname.substring(declaredPackage.length()+1); + } else { + debug("false[COMPILATION_UNIT; bad declaredPackage = %s] InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", declaredPackage, cname, tree); + return false; + } + } + break; + case CLASS: + case INTERFACE: + case ENUM: + case ANNOTATION_TYPE: + if (i > 0 && trees.get(i - 1).getKind() == Tree.Kind.NEW_CLASS) { + // For an anonymous class, the CLASS tree is always directly inside of + // a NEW_CLASS tree. If that's the case here then skip this iteration + // since we've already looked at the new class tree in the previous + // iteration. + break; + } + debug("InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + + if (i > 0 && trees.get(i - 1).getKind() == Tree.Kind.BLOCK) { + // Section 14.3 of the JLS says "every local class declaration + // statement is immediately contained by a block". + checkLocal = true; + debug("found local class: InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + break; + } + + // all four Kinds are represented by ClassTree + ClassTree c = (ClassTree)tree; + Name csn = c.getSimpleName(); + + if (csn == null || csn.length() == 0) { + debug("empty getSimpleName: InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + checkAnon = true; + break; + } + String treeClassName = csn.toString(); + if (cname.equals(treeClassName)) { + if (exactMatch) { + cname = ""; + } else { + debug("true InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + return true; + } + } else if (cname.startsWith(treeClassName + "$") + || (cname.startsWith(treeClassName + "."))) { + cname = cname.substring(treeClassName.length()+1); + } else if (!treeClassName.isEmpty()) { + // treeClassName is empty for anonymous inner class + // System.out.println("cname else: " + cname); + debug("false InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + return false; + } + break; + case NEW_CLASS: + // When matching the "new Class() { ... }" expression itself, we + // should not use the anonymous class name. But when matching + // within the braces, we should. + debug("InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + if (cname.equals("")) { + insideMatch = true; + } else { + NewClassTree nc = (NewClassTree) tree; + checkAnon = nc.getClassBody() != null; + } + break; + case METHOD: + case VARIABLE: + // Avoid searching inside inner classes of the matching class, + // lest a homographic inner class lead to a spurious match. + if (insideMatch) { + debug("false InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + return false; + } + break; + default: + // nothing to do + break; + } + + if (checkAnon) { + // If block is anonymous class, and cname starts with an + // anonymous class index, see if they match. + + Matcher anonclassMatcher = anonclassPattern.matcher(cname); + if (! anonclassMatcher.matches()) { + debug("false[anonclassMatcher] InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + return false; + } + // for JDK 7: String anonclassNumString = anonclassMatcher.group("num"); + // for JDK 7: cname = anonclassMatcher.group("remaining"); + String anonclassNumString = anonclassMatcher.group(1); + cname = anonclassMatcher.group(3); + if (cname == null) { + cname = ""; + } + int anonclassNum; + try { + anonclassNum = Integer.parseInt(anonclassNumString); + } catch (NumberFormatException e) { + throw new Error("This can't happen: " + cname + "$" + anonclassNumString, e); + } + + int actualIndexInSource = AnonymousClassScanner.indexOfClassTree(path, tree); + + if (anonclassNum != actualIndexInSource) { + debug("false[anonclassNum %d %d] InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", anonclassNum, actualIndexInSource, cname, tree); + return false; + } + } else if (checkLocal) { + ClassTree c = (ClassTree) tree; + String treeClassName = c.getSimpleName().toString(); + + Matcher localClassMatcher = localClassPattern.matcher(cname); + if (!localClassMatcher.matches()) { + debug("false[localClassMatcher] InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname, tree); + return false; + } + String localClassNumString = localClassMatcher.group(1); + String localClassName = localClassMatcher.group(2); + int localClassNum = Integer.parseInt(localClassNumString); + + int actualIndexInSource = LocalClassScanner.indexOfClassTree(path, c); + + if (actualIndexInSource == localClassNum && treeClassName.startsWith(localClassName)) { + cname = localClassMatcher.group(4); + if (cname == null) { + cname = ""; + } + } else { + debug("false[localClassNum %d %d] InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", localClassNum, actualIndexInSource, cname, tree); + return false; + } + } + } + + debug("%s InClassCriterion.isSatisfiedBy:%n cname=%s%n tree=%s%n", cname.equals(""), cname, path.getLeaf()); + return cname.equals(""); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "In class '" + className + "'" + (exactMatch ? " (exactly)" : ""); + } + + /** + * Return an array of Strings representing the characters between + * successive instances of the delimiter character. + * Always returns an array of length at least 1 (it might contain only the + * empty string). + * @see #split(String s, String delim) + */ + /* + private static List<String> split(String s, char delim) { + List<String> result = new ArrayList<String>(); + for (int delimpos = s.indexOf(delim); delimpos != -1; delimpos = s.indexOf(delim)) { + result.add(s.substring(0, delimpos)); + s = s.substring(delimpos+1); + } + result.add(s); + return result; + } + */ + + private static void debug(String message, Object... args) { + if (debug) { + System.out.printf(message, args); + } + } + +}
diff --git a/annotation-file-utilities/src/annotator/find/InFieldInitCriterion.java b/annotation-file-utilities/src/annotator/find/InFieldInitCriterion.java new file mode 100644 index 0000000..246baba --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/InFieldInitCriterion.java
@@ -0,0 +1,49 @@ +package annotator.find; + +import annotator.scanner.CommonScanner; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * Criterion for being within a specific field initializer. + */ +public class InFieldInitCriterion implements Criterion { + + public final String varName; + public final Criterion varCriterion; + + public InFieldInitCriterion(String varName) { + this.varName = varName; + this.varCriterion = Criteria.is(Tree.Kind.VARIABLE, varName); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + while (path != null) { + if (CommonScanner.isFieldInit(path)) { + return varCriterion.isSatisfiedBy(path); + } + path = path.getParentPath(); + } + return false; + } + + @Override + public Kind getKind() { + return Kind.IN_FIELD_INIT; + } + + @Override + public String toString() { + return "In field initializer for field '" + varName + "'"; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/InInitBlockCriterion.java b/annotation-file-utilities/src/annotator/find/InInitBlockCriterion.java new file mode 100644 index 0000000..51f0677 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/InInitBlockCriterion.java
@@ -0,0 +1,55 @@ +package annotator.find; + +import annotator.scanner.CommonScanner; +import annotator.scanner.InitBlockScanner; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * Criterion for being within a specific initializer. + * + * @author dbro + */ +public class InInitBlockCriterion implements Criterion { + public final int blockID; + public final boolean isStatic; + public final Criterion notInMethodCriterion; + + public InInitBlockCriterion(int blockID, boolean isStatic) { + this.blockID = blockID; + this.isStatic = isStatic; + this.notInMethodCriterion = Criteria.notInMethod(); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + while (path != null) { + if (CommonScanner.isInitBlock(path, isStatic)) { + int indexInSource = InitBlockScanner.indexOfInitTree(path, isStatic); + return indexInSource == blockID; + } + path = path.getParentPath(); + } + return false; + } + + @Override + public Kind getKind() { + return isStatic ? Kind.IN_STATIC_INIT : Kind.IN_INSTANCE_INIT; + } + + @Override + public String toString() { + return "In " + (isStatic ? "static" : "instance") + + " initializer with index " + blockID; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/InMethodCriterion.java b/annotation-file-utilities/src/annotator/find/InMethodCriterion.java new file mode 100644 index 0000000..d70c7f5 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/InMethodCriterion.java
@@ -0,0 +1,73 @@ +package annotator.find; + +import javax.lang.model.element.Modifier; + +import annotator.Main; + +import com.sun.source.tree.*; +import com.sun.source.util.TreePath; + +/** + * Represents the criterion that a program element is in a method with a + * certain name. + */ +final class InMethodCriterion implements Criterion { + + public final String name; + private final IsSigMethodCriterion sigMethodCriterion; + + InMethodCriterion(String name) { + this.name = name; + sigMethodCriterion = new IsSigMethodCriterion(name); + } + + /** + * {@inheritDoc} + */ + @Override + public Kind getKind() { + return Kind.IN_METHOD; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + Criteria.dbug.debug("InMethodCriterion.isSatisfiedBy(%s); this=%s%n", + Main.pathToString(path), this.toString()); + boolean staticDecl = false; + boolean result = false; + + do { + if (path.getLeaf().getKind() == Tree.Kind.METHOD) { + boolean b = sigMethodCriterion.isSatisfiedBy(path); + Criteria.dbug.debug("%s%n", "InMethodCriterion.isSatisfiedBy => b"); + return b; + } + if (path.getLeaf().getKind() == Tree.Kind.VARIABLE) { + ModifiersTree mods = ((VariableTree) path.getLeaf()).getModifiers(); + staticDecl = mods.getFlags().contains(Modifier.STATIC); + } + path = path.getParentPath(); + } while (path != null && path.getLeaf() != null); + + result = (staticDecl ? "<clinit>()V" : "<init>()V").equals(name); + + Criteria.dbug.debug("InMethodCriterion.isSatisfiedBy => %s%n", result); + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "in method '" + name + "'"; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/InPackageCriterion.java b/annotation-file-utilities/src/annotator/find/InPackageCriterion.java new file mode 100644 index 0000000..5ab88d9 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/InPackageCriterion.java
@@ -0,0 +1,71 @@ +package annotator.find; + +import annotator.Main; + +import com.sun.source.tree.*; +import com.sun.source.util.TreePath; + +/** + * Represents the criterion that a program element is in a package with a + * certain name. + */ +final class InPackageCriterion implements Criterion { + + private final String name; + + InPackageCriterion(String name) { + this.name = name; + } + + /** + * {@inheritDoc} + */ + @Override + public Kind getKind() { + return Kind.IN_PACKAGE; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Criteria.dbug.debug("InPackageCriterion.isSatisfiedBy(%s); this=%s", + Main.pathToString(path), this.toString()); + + do { + Tree tree = path.getLeaf(); + if (tree.getKind() == Tree.Kind.COMPILATION_UNIT) { + CompilationUnitTree cu = (CompilationUnitTree)tree; + ExpressionTree pn = cu.getPackageName(); + if (pn == null) { + return name == null || name.equals(""); + } else { + String packageName = pn.toString(); + return name != null && (name.equals(packageName)); + } + } + path = path.getParentPath(); + } while (path != null && path.getLeaf() != null); + + Criteria.dbug.debug("InPackageCriterion.isSatisfiedBy => false"); + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "in package '" + name + "'"; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/Insertion.java b/annotation-file-utilities/src/annotator/find/Insertion.java new file mode 100644 index 0000000..68a1b2f --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/Insertion.java
@@ -0,0 +1,554 @@ +package annotator.find; + +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import annotations.io.ASTPath; + +import com.sun.source.tree.Tree; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +import plume.Pair; +import type.ArrayType; +import type.DeclaredType; +import type.Type; +import type.BoundedType; + +/** + * Specifies something that needs to be inserted into a source file, including + * the "what" and the "where". + */ +public abstract class Insertion { + + public enum Kind { + ANNOTATION, + CAST, + CONSTRUCTOR, + METHOD, + NEW, + RECEIVER, + CLOSE_PARENTHESIS + } + + private final Criteria criteria; + // If non-null, then try to put annotation on its own line, + // horizontally aligned with the location. + private final boolean separateLine; + + /** + * Whether this insertion has already been inserted into source code. + */ + private boolean inserted; + + /** + * The package names for the annotations being inserted by this Insertion. + * This will be empty unless {@link #getText(boolean, boolean)} is called + * with abbreviate true. + */ + protected Set<String> packageNames; + + /** + * Set of annotation names that should always be qualified, even + * when {@link getText(boolean, boolean)} is called with abbreviate true. + */ + protected static Set<String> alwaysQualify = new LinkedHashSet<String>(); + + /** + * Creates a new insertion. + * + * @param criteria where to insert the text + * @param separateLine whether to insert the text on its own + */ + public Insertion(Criteria criteria, boolean separateLine) { + this.criteria = criteria; + this.separateLine = separateLine; + this.packageNames = new LinkedHashSet<String>(); + this.inserted = false; + } + + /** + * Gets the insertion criteria. + * + * @return the criteria + */ + public Criteria getCriteria() { + return criteria; + } + + /** + * Gets the insertion text (not commented or abbreviated, and without added + * leading or trailing whitespace). + * + * @return the text to insert + */ + public String getText() { + return getText(false, false, true, 0, '\0'); + } + + /** + * Gets the insertion text with a leading and/or trailing space added based + * on the values of the {@code gotSeparateLine}, {@code pos}, and + * {@code precedingChar} parameters. + * + * @param comments + * if true, Java 8 features will be surrounded in comments + * @param abbreviate + * if true, the package name will be removed from the annotations. + * The package name can be retrieved again by calling the + * {@link #getPackageNames()} method. + * @param gotSeparateLine + * {@code true} if this insertion is actually added on a separate + * line. + * @param pos + * the source position where this insertion will be inserted + * @param precedingChar + * the character directly preceding where this insertion will be + * inserted. This value will be ignored if {@code pos} is 0. + * + * @return the text to insert + */ + public String getText(boolean comments, boolean abbreviate, + boolean gotSeparateLine, int pos, char precedingChar) { + String toInsert = getText(comments, abbreviate); + if (!toInsert.isEmpty()) { + if (addLeadingSpace(gotSeparateLine, pos, precedingChar)) { + toInsert = " " + toInsert; + } + if (addTrailingSpace(gotSeparateLine)) { + toInsert = toInsert + " "; + } + } + return toInsert; + } + + /** + * Gets the insertion text. + * + * @param comments + * if true, Java 8 features will be surrounded in comments + * @param abbreviate + * if true, the package name will be removed from the annotations. + * The package name can be retrieved again by calling the + * {@link #getPackageNames()} method. + * @return the text to insert + */ + protected abstract String getText(boolean comments, boolean abbreviate); + + /** + * Indicates if a preceding space should be added to this insertion. + * Subclasses may override this method for custom leading space rules. + * + * @param gotSeparateLine + * {@code true} if this insertion is actually added on a separate + * line. + * @param pos + * the source position where this insertion will be inserted + * @param precedingChar + * the character directly preceding where this insertion will be + * inserted. This value will be ignored if {@code pos} is 0. + * @return {@code true} if a leading space should be added, {@code false} + * otherwise. + */ + protected boolean addLeadingSpace(boolean gotSeparateLine, int pos, + char precedingChar) { + // Don't add a preceding space if this insertion is on its own line, + // it's at the beginning of the file, the preceding character is already + // whitespace, or the preceding character is the first formal or generic + // parameter. + return !gotSeparateLine && pos != 0 + && !Character.isWhitespace(precedingChar) + && precedingChar != '(' && precedingChar != '<'; + } + + /** + * Indicates if a trailing space should be added to this insertion. + * Subclasses may override this method for custom trailing space rules. + * + * @param gotSeparateLine + * {@code true} if this insertion is actually added on a separate + * line. + * @return {@code} true if a trailing space should be added, {@code false} + * otherwise. + */ + protected boolean addTrailingSpace(boolean gotSeparateLine) { + // Don't added a trailing space if this insertion is on its own line. + return !gotSeparateLine; + } + + /** + * Gets the package name. + * + * @return the package name of the annotation being inserted by this + * Insertion. This will be empty unless + * {@link #getText(boolean, boolean)} is called with abbreviate true. + */ + public Set<String> getPackageNames() { + return packageNames; + } + + /** + * Gets the set of annotation names that should always be qualified. + */ + public static Set<String> getAlwaysQualify() { + return alwaysQualify; + } + + /** + * Sets the set of annotation names that should always be qualified. + */ + public static void setAlwaysQualify(Set<String> set) { + alwaysQualify = set; + } + + /** + * Gets whether the insertion goes on a separate line. + * + * @return whether the insertion goes on a separate line + */ + public boolean getSeparateLine() { + return separateLine; + } + + /** + * Gets whether this insertion has already been inserted into source code. + * @return {@code true} if this insertion has already been inserted, + * {@code false} otherwise. + */ + public boolean getInserted() { + return inserted; + } + + /** + * Sets whether this insertion has already been inserted into source code. + * @param inserted {@code true} if this insertion has already been inserted, + * {@code false} otherwise. + */ + public void setInserted(boolean inserted) { + this.inserted = inserted; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return String.format("(nl=%b) @ %s", separateLine, criteria); + } + + /** + * Gets the kind of this insertion. + */ + public abstract Kind getKind(); + + /** + * Removes the leading package. + * + * @return given <code>@com.foo.bar(baz)</code> it returns the pair + * <code>{ com.foo, @bar(baz) }</code>. + */ + public static Pair<String, String> removePackage(String s) { + int nameEnd = s.indexOf("("); + if (nameEnd == -1) { + nameEnd = s.length(); + } + int dotIndex = s.lastIndexOf(".", nameEnd); + if (dotIndex != -1) { + String basename = s.substring(dotIndex + 1); + if (!alwaysQualify.contains(basename)) { + String packageName = s.substring(0, nameEnd); + if (packageName.startsWith("@")) { + return Pair.of(packageName.substring(1), + "@" + basename); + } else { + return Pair.of(packageName, basename); + } + } + } + return Pair.of((String) null, s); + } + + /** + * Converts the given type to a String. This method can't be in the + * {@link Type} class because this method relies on the {@link Insertion} + * class to format annotations, and the {@link Insertion} class is not + * available from {@link Type}. + * + * @param type + * the type to convert + * @param comments + * if true, Java 8 features will be surrounded in comments + * @param abbreviate + * if true, the package name will be removed from the annotations. + * The package name can be retrieved again by calling the + * {@link #getPackageNames()} method. + * @return the type as a string + */ + public String typeToString(Type type, boolean comments, boolean abbreviate) { + StringBuilder result = new StringBuilder(); + + switch (type.getKind()) { + case DECLARED: + DeclaredType declaredType = (DeclaredType) type; + String typeName = declaredType.getName(); + int sep = typeName.lastIndexOf('.') + 1; + if (abbreviate) { + typeName = typeName.substring(sep); + } else if (sep > 0) { + result.append(typeName.substring(0, sep)); + typeName = typeName.substring(sep); + } + writeAnnotations(type, result, comments, abbreviate); + result.append(typeName); + if (!declaredType.isWildcard()) { + List<Type> typeArguments = declaredType.getTypeParameters(); + if (!typeArguments.isEmpty()) { + result.append('<'); + result.append(typeToString(typeArguments.get(0), comments, abbreviate)); + for (int i = 1; i < typeArguments.size(); i++) { + result.append(", "); + result.append(typeToString(typeArguments.get(i), comments, abbreviate)); + } + result.append('>'); + } + Type innerType = declaredType.getInnerType(); + if (innerType != null) { + result.append('.'); + result.append(typeToString(innerType, comments, abbreviate)); + } + } + break; + case ARRAY: + ArrayType arrayType = (ArrayType) type; + result.append(typeToString(arrayType.getComponentType(), comments, abbreviate)); + if (!arrayType.getAnnotations().isEmpty()) { + result.append(' '); + } + writeAnnotations(type, result, comments, abbreviate); + result.append("[]"); + break; + case BOUNDED: + BoundedType boundedType = (BoundedType) type; + result.append(typeToString(boundedType.getName(), comments, abbreviate)); + result.append(' '); + result.append(boundedType.getBoundKind()); + result.append(' '); + result.append(typeToString(boundedType.getBound(), comments, abbreviate)); + break; + default: + throw new RuntimeException("Illegal kind: " + type.getKind()); + } + // There will be extra whitespace at the end if this is only annotations, so trim + return result.toString().trim(); + } + + /** + * Writes the annotations on the given type to the given + * {@link StringBuilder}. + * + * @param type + * contains the annotations to write. Only the annotations + * directly on the type will be written. Subtypes will be + * ignored. + * @param result + * where to write the annotations + * @param comments + * if {@code true}, Java 8 features will be surrounded in + * comments. + * @param abbreviate + * if {@code true}, the package name will be removed from the + * annotations. The package name can be retrieved again by + * calling the {@link #getPackageNames()} method. + */ + private void writeAnnotations(Type type, StringBuilder result, + boolean comments, boolean abbreviate) { + for (String annotation : type.getAnnotations()) { + AnnotationInsertion ins = new AnnotationInsertion(annotation); + result.append(ins.getText(comments, abbreviate)); + result.append(" "); + if (abbreviate) { + packageNames.addAll(ins.getPackageNames()); + } + } + } + + /** + * Adds each of the given inner type insertions to the correct part of the + * type, based on the insertion's type path. + * + * @param innerTypeInsertions + * the insertions to add to the type. These must be inner type + * insertions, meaning each of the insertions' {@link Criteria} + * must contain a {@link GenericArrayLocationCriterion} and + * {@link GenericArrayLocationCriterion#getLocation()} must return a + * non-empty list. + * @param outerType the type to add the insertions to + */ + public static void decorateType(List<Insertion> innerTypeInsertions, final Type outerType) { + decorateType(innerTypeInsertions, outerType, null); + } + + public static void decorateType(List<Insertion> innerTypeInsertions, + final Type outerType, ASTPath outerPath) { + for (Insertion innerInsertion : innerTypeInsertions) { + // Set each annotation as inserted (even if it doesn't actually get + // inserted because of an error) to "disable" the insertion in the global + // insertion list. + innerInsertion.setInserted(true); + + try { + if (innerInsertion.getKind() != Insertion.Kind.ANNOTATION) { + throw new RuntimeException("Expected 'ANNOTATION' insertion kind, got '" + + innerInsertion.getKind() + "'."); + } + GenericArrayLocationCriterion c = innerInsertion.getCriteria().getGenericArrayLocation(); + String annos = + ((AnnotationInsertion) innerInsertion).getAnnotation(); + if (c == null) { + ASTPath astPath = innerInsertion.getCriteria().getASTPath(); + if (outerPath != null && astPath != null) { + decorateType(astPath, annos, outerType, outerPath); + continue; + } + throw new RuntimeException("Missing type path."); + } + + List<TypePathEntry> location = c.getLocation(); + Type type = outerType; + + // Use the type path entries to traverse through the type. Throw an + // exception and move on to the next inner type insertion if the type + // path and actual type don't match up. + for (TypePathEntry tpe : location) { + switch (tpe.tag) { + case ARRAY: + if (type.getKind() == Type.Kind.ARRAY) { + type = ((ArrayType) type).getComponentType(); + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + case INNER_TYPE: + if (type.getKind() == Type.Kind.DECLARED) { + DeclaredType declaredType = (DeclaredType) type; + if (declaredType.getInnerType() == null) { + throw new RuntimeException("Incorrect type path: " + + "expected inner type but none exists."); + } + type = declaredType.getInnerType(); + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + case WILDCARD: + if (type.getKind() == Type.Kind.BOUNDED) { + BoundedType boundedType = (BoundedType) type; + if (boundedType.getBound() == null) { + throw new RuntimeException("Incorrect type path: " + + "expected type bound but none exists."); + } + type = boundedType.getBound(); + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + case TYPE_ARGUMENT: + if (type.getKind() == Type.Kind.DECLARED) { + DeclaredType declaredType = (DeclaredType) type; + if (0 <= tpe.arg && tpe.arg < + declaredType.getTypeParameters().size()) { + type = declaredType.getTypeParameter(tpe.arg); + } else { + throw new RuntimeException("Incorrect type argument index: " + tpe.arg); + } + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + default: + throw new RuntimeException("Illegal TypePathEntryKind: " + tpe.tag); + } + } + if (type.getKind() == Type.Kind.BOUNDED) { + // Annotations aren't allowed directly on the BoundedType, see BoundedType + type = ((BoundedType) type).getName(); + } + type.addAnnotation(annos); + } catch (Throwable e) { + TreeFinder.reportInsertionError(innerInsertion, e); + } + } + } + + private static void decorateType(ASTPath astPath, + String annos, Type type, ASTPath outerPath) { + // type.addAnnotation(annos); // TODO + Iterator<ASTPath.ASTEntry> ii = astPath.iterator(); + Iterator<ASTPath.ASTEntry> oi = outerPath.iterator(); + + while (oi.hasNext()) { + if (!ii.hasNext() || !oi.next().equals(ii.next())) { + throw new RuntimeException("Incorrect AST path."); + } + } + + while (ii.hasNext()) { + ASTPath.ASTEntry entry = ii.next(); + Tree.Kind kind = entry.getTreeKind(); + switch (kind) { + case ARRAY_TYPE: + if (type.getKind() == Type.Kind.ARRAY) { + type = ((ArrayType) type).getComponentType(); + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + case MEMBER_SELECT: + if (type.getKind() == Type.Kind.DECLARED) { + DeclaredType declaredType = (DeclaredType) type; + if (declaredType.getInnerType() == null) { + throw new RuntimeException("Incorrect type path: " + + "expected inner type but none exists."); + } + type = declaredType.getInnerType(); + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + case PARAMETERIZED_TYPE: + if (type.getKind() == Type.Kind.DECLARED) { + int arg = entry.getArgument(); + DeclaredType declaredType = (DeclaredType) type; + if (0 <= arg && arg < declaredType.getTypeParameters().size()) { + type = declaredType.getTypeParameter(arg); + } else { + throw new RuntimeException("Incorrect type argument index: " + arg); + } + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + case UNBOUNDED_WILDCARD: + if (type.getKind() == Type.Kind.BOUNDED) { + BoundedType boundedType = (BoundedType) type; + if (boundedType.getBound() == null) { + throw new RuntimeException("Incorrect type path: " + + "expected type bound but none exists."); + } + type = boundedType.getBound(); + } else { + throw new RuntimeException("Incorrect type path."); + } + break; + default: + throw new RuntimeException("Illegal TreeKind: " + kind); + } + } + if (type.getKind() == Type.Kind.BOUNDED) { + // Annotations aren't allowed directly on the BoundedType, see BoundedType + type = ((BoundedType) type).getName(); + } + type.addAnnotation(annos); + } +}
diff --git a/annotation-file-utilities/src/annotator/find/Insertions.java b/annotation-file-utilities/src/annotator/find/Insertions.java new file mode 100644 index 0000000..0f70e2a --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/Insertions.java
@@ -0,0 +1,1700 @@ +package annotator.find; + +import java.util.AbstractSet; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.lang.model.element.Name; +import javax.lang.model.type.TypeKind; + +import annotations.el.InnerTypeLocation; +import annotations.io.ASTIndex; +import annotations.io.ASTPath; +import annotations.io.ASTRecord; +import type.*; + +import com.sun.source.tree.AnnotatedTypeTree; +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.ArrayTypeTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.ParameterizedTypeTree; +import com.sun.source.tree.PrimitiveTypeTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.TreeVisitor; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.tree.WildcardTree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.code.Kinds; +import com.sun.tools.javac.code.Symbol.ClassSymbol; +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind; +import com.sun.tools.javac.code.TypeTag; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.util.Pair; + +/** + * @author dbro + * + * An indexed collection (though not {@link java.util.Collection}, only + * {@link java.lang.Iterable}) of {@link Insertion}s with methods to + * select those specified for a given class or for an outer class along + * with its local classes. This class is especially useful when a + * single JAIF stores annotations for many source files, as it reduces + * the number of insertions to be considered for any AST node. + * + * The class now serves a second purpose, which should probably be + * separated out (according to OO dogma, at least): It attaches + * {@link annotations.io.ASTPath}-based inner type {@link Insertion}s to + * a {@link TypedInsertion} on the outer type if one exists (see + * {@link #organizeTypedInsertions(CompilationUnitTree, String, Collection)}. + * Since getting these insertions right depends on this organization, + * this class is now essential for correctness, not merely for + * performance. + */ +public class Insertions implements Iterable<Insertion> { + private static int kindLevel(Insertion i) { + // ordered so insertion that depends on another gets inserted after other + switch (i.getKind()) { + case CONSTRUCTOR: + return 3; + case NEW: + case RECEIVER: + return 2; + case CAST: + return 1; + // case ANNOTATION: + // case CLOSE_PARENTHESIS: + default: + return 0; + } + } + + private static final Comparator<Insertion> byASTRecord = + new Comparator<Insertion>() { + @Override + public int compare(Insertion o1, Insertion o2) { + Criteria c1 = o1.getCriteria(); + Criteria c2 = o2.getCriteria(); + ASTPath p1 = c1.getASTPath(); + ASTPath p2 = c2.getASTPath(); + ASTRecord r1 = new ASTRecord(null, + c1.getClassName(), c1.getMethodName(), c1.getFieldName(), + p1 == null ? ASTPath.empty() : p1); + ASTRecord r2 = new ASTRecord(null, + c2.getClassName(), c2.getMethodName(), c2.getFieldName(), + p2 == null ? ASTPath.empty() : p2); + int c = r1.compareTo(r2); + if (c == 0) { + // c = o1.getKind().compareTo(o2.getKind()); + c = Integer.compare(kindLevel(o2), kindLevel(o1)); // descending + if (c == 0) { c = o1.toString().compareTo(o2.toString()); } + } + return c; + } + }; + + // store indexes insertions by (qualified) outer class name and inner + // class path (if any) + private Map<String, Map<String, Set<Insertion>>> store; + private int size; + + private Pair<String, String> nameSplit(String name) { + int i = name.indexOf('$'); // FIXME: don't split on '$' in source + return i < 0 + ? Pair.of(name, "") + : Pair.of(name.substring(0, i), name.substring(i)); + } + + public Insertions() { + store = new HashMap<String, Map<String, Set<Insertion>>>(); + size = 0; + } + + // auxiliary for following two methods + private void forClass(CompilationUnitTree cut, + String qualifiedClassName, Set<Insertion> result) { + Pair<String, String> pair = nameSplit(qualifiedClassName); + Map<String, Set<Insertion>> map = store.get(pair.fst); + if (map != null) { + Set<Insertion> set = new TreeSet<Insertion>(byASTRecord); + set.addAll(map.get(pair.snd)); + if (set != null) { + set = organizeTypedInsertions(cut, qualifiedClassName, set); + result.addAll(set); + } + } + } + + /** + * Selects {@link Insertion}s relevant to a given class. + * + * @param cut the current compilation unit + * @param qualifiedClassName the fully qualified class name + * @return {@link java.util.Set} of {@link Insertion}s with an + * {@link InClassCriterion} for the given class + */ + public Set<Insertion> forClass(CompilationUnitTree cut, + String qualifiedClassName) { + Set<Insertion> set = new LinkedHashSet<Insertion>(); + forClass(cut, qualifiedClassName, set); + return set; + } + + /** + * Selects {@link Insertion}s relevant to a given outer class and its + * local classes. + * + * @param cut the current compilation unit + * @param qualifiedOuterClassName the fully qualified outer class name + * @return {@link java.util.Set} of {@link Insertion}s with an + * {@link InClassCriterion} for the given outer class or one + * of its local classes + */ + public Set<Insertion> forOuterClass(CompilationUnitTree cut, + String qualifiedOuterClassName) { + Map<String, Set<Insertion>> map = store.get(qualifiedOuterClassName); + if (map == null || map.isEmpty()) { + return Collections.<Insertion>emptySet(); + } else { + Set<Insertion> set = new LinkedHashSet<Insertion>(); + for (String key : map.keySet()) { + String qualifiedClassName = qualifiedOuterClassName + key; + forClass(cut, qualifiedClassName, set); + } + return set; + } + } + + /** + * Add an {@link Insertion} to this collection. + */ + public void add(Insertion ins) { + InClassCriterion icc = ins.getCriteria().getInClass(); + String k1 = ""; + String k2 = ""; + Map<String, Set<Insertion>> map; + Set<Insertion> set; + + if (icc != null) { + Pair<String, String> triple = nameSplit(icc.className); + k1 = triple.fst; + k2 = triple.snd; + } + map = store.get(k1); + if (map == null) { + map = new HashMap<String, Set<Insertion>>(); + store.put(k1, map); + } + set = map.get(k2); + if (set == null) { + set = new LinkedHashSet<Insertion>(); + map.put(k2, set); + } + + size -= set.size(); + set.add(ins); + size += set.size(); + } + + /** + * Add all {@link Insertion}s in the given + * {@link java.util.Collection} to this collection. + */ + public void addAll(Collection<? extends Insertion> c) { + for (Insertion ins : c) { + add(ins); + } + } + + /** + * Returns the number of {@link Insertion}s in this collection. + */ + public int size() { + return size; + } + + @Override + public Iterator<Insertion> iterator() { + return new Iterator<Insertion>() { + private Iterator<Map<String, Set<Insertion>>> miter = + store.values().iterator(); + private Iterator<Set<Insertion>> siter = + Collections.<Set<Insertion>>emptySet().iterator(); + private Iterator<Insertion> iiter = + Collections.<Insertion>emptySet().iterator(); + + @Override + public boolean hasNext() { + if (iiter.hasNext()) { return true; } + while (siter.hasNext()) { + iiter = siter.next().iterator(); + if (iiter.hasNext()) { return true; } + } + while (miter.hasNext()) { + siter = miter.next().values().iterator(); + while (siter.hasNext()) { + iiter = siter.next().iterator(); + if (iiter.hasNext()) { return true; } + } + } + return false; + } + + @Override + public Insertion next() { + if (hasNext()) { return iiter.next(); } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Returns a {@link java.util.List} containing all {@link Insertion}s + * in this collection. + */ + public List<Insertion> toList() { + List<Insertion> list = new ArrayList<Insertion>(size); + for (Insertion ins : this) { list.add(ins); } + return null; + } + + /* + * This method detects inner type relationships among ASTPath-based + * insertion specifications and organizes the insertions accordingly. + * This step is necessary because 1) insertion proceeds from the end to + * the beginning of the source and 2) the insertion location does not + * always exist prior to the top-level type insertion. + */ + private Set<Insertion> organizeTypedInsertions(CompilationUnitTree cut, + String className, Collection<Insertion> insertions) { + ASTRecordMap<TypedInsertion> map = new ASTRecordMap<TypedInsertion>(); + Set<Insertion> organized = new LinkedHashSet<Insertion>(); + Set<Insertion> unorganized = new LinkedHashSet<Insertion>(); + List<Insertion> list = new ArrayList<Insertion>(); + + // First divide the insertions into three buckets: TypedInsertions + // on outer types (map), ASTPath-based insertions on local types + // (unorganized -- built as list and then sorted, since building as + // a set spuriously removes "duplicates" according to the + // comparator), and everything else (organized -- where all + // eventually land). + for (Insertion ins : insertions) { + if (ins.getInserted()) { continue; } + Criteria criteria = ins.getCriteria(); + GenericArrayLocationCriterion galc = + criteria.getGenericArrayLocation(); + ASTPath p = criteria.getASTPath(); + if (p == null || p.isEmpty() + || galc != null && !galc.getLocation().isEmpty() + || ins instanceof CastInsertion + || ins instanceof CloseParenthesisInsertion) { + organized.add(ins); + } else { + ASTRecord rec = new ASTRecord(cut, criteria.getClassName(), + criteria.getMethodName(), criteria.getFieldName(), p); + ASTPath.ASTEntry entry = rec.astPath.get(-1); + Tree node; + + if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY + && entry.childSelectorIs(ASTPath.TYPE) + && entry.getArgument() == 0) { + ASTPath temp = rec.astPath.getParentPath(); + node = ASTIndex.getNode(cut, rec.replacePath(temp)); + node = node instanceof JCTree.JCNewArray + ? TypeTree.fromType(((JCTree.JCNewArray) node).type) + : null; + } else { + node = ASTIndex.getNode(cut, rec); + } + + if (ins instanceof TypedInsertion) { + TypedInsertion tins = map.get(rec); + if (ins instanceof NewInsertion) { + NewInsertion nins = (NewInsertion) ins; + if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY + && entry.childSelectorIs(ASTPath.TYPE)) { + int a = entry.getArgument(); + List<TypePathEntry> loc0 = new ArrayList<TypePathEntry>(a); + ASTRecord rec0 = null; + if (a == 0) { + rec0 = rec.replacePath(p.getParentPath()); + Tree t = ASTIndex.getNode(cut, rec0); + if (t == null || t.toString().startsWith("{")) { + rec0 = null; + } else { + rec = rec0; + rec0 = rec.extend(Tree.Kind.NEW_ARRAY, + ASTPath.TYPE, 0); + } + } else if (node != null + && !nins.getInnerTypeInsertions().isEmpty()) { + if (node.getKind() == Tree.Kind.IDENTIFIER) { + node = ASTIndex.getNode(cut, + rec.replacePath(p.getParentPath())); + } + if ((node.getKind() == Tree.Kind.NEW_ARRAY + || node.getKind() == Tree.Kind.ARRAY_TYPE) + && !node.toString().startsWith("{")) { + rec = rec.replacePath(p.getParentPath()); + + Collections.fill(loc0, TypePathEntry.ARRAY); + // irec = rec; + // if (node.getKind() == Tree.Kind.NEW_ARRAY) { + rec0 = rec.extend(Tree.Kind.NEW_ARRAY, + ASTPath.TYPE, 0); + // } + } + } + + if (rec0 != null) { + for (Insertion inner : nins.getInnerTypeInsertions()) { + Criteria icriteria = inner.getCriteria(); + GenericArrayLocationCriterion igalc = + icriteria.getGenericArrayLocation(); + if (igalc != null) { + ASTRecord rec1; + int b = igalc.getLocation().size(); + List<TypePathEntry> loc = + new ArrayList<TypePathEntry>(a + b); + loc.addAll(loc0); + loc.addAll(igalc.getLocation()); + rec1 = extendToInnerType(rec0, loc, node); + icriteria.add(new GenericArrayLocationCriterion()); + icriteria.add(new ASTPathCriterion(rec1.astPath)); + inner.setInserted(false); + organized.add(inner); + } + } + nins.getInnerTypeInsertions().clear(); + } + } + } + if (tins == null) { + map.put(rec, (TypedInsertion) ins); + } else if (tins.getType().equals(((TypedInsertion) ins).getType())) { + mergeTypedInsertions(tins, (TypedInsertion) ins); + } + } else { + int d = newArrayInnerTypeDepth(p); + if (d > 0) { + ASTPath temp = p; + while (!temp.isEmpty() + && (node == null || node.getKind() != Tree.Kind.NEW_ARRAY)) { + // TODO: avoid repeating work of newArrayInnerTypeDepth() + temp = temp.getParentPath(); + node = ASTIndex.getNode(cut, rec.replacePath(temp)); + } + if (node == null) { + // TODO: ??? + } + temp = temp.extend( + new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0)); + if (node.toString().startsWith("{")) { + TypedInsertion tins = map.get(rec.replacePath(temp)); + if (tins == null) { + // TODO + } else { + tins.getInnerTypeInsertions().add(ins); + ins.setInserted(true); + } + } else { + List<? extends ExpressionTree> dims = + ((NewArrayTree) node).getDimensions(); + ASTRecord irec = rec.replacePath(p.getParentPath()) + .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0); + GenericArrayLocationCriterion igalc = + criteria.getGenericArrayLocation(); + for (int i = 0 ; i < d; i++) { + irec = irec.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE); + } + if (igalc != null) { + List<TypePathEntry> loc = igalc.getLocation(); + if (!loc.isEmpty()) { + try { + Tree dim = dims.get(d-1); + irec = extendToInnerType(irec, loc, dim); + criteria.add(new ASTPathCriterion(irec.astPath)); + criteria.add(new GenericArrayLocationCriterion()); + } catch (RuntimeException e) {} + } + } + } + } + list.add(ins); + } + } + } + // if (map.isEmpty()) { + // organized.addAll(unorganized); + // return organized; + // } + Collections.sort(list, byASTRecord); + unorganized.addAll(list); + + // Each Insertion in unorganized gets attached to a TypedInsertion + // in map if possible; otherwise, it gets dumped into organized. + for (Insertion ins : unorganized) { + Criteria criteria = ins.getCriteria(); + String methodName = criteria.getMethodName(); + String fieldName = criteria.getFieldName(); + ASTPath ap1 = criteria.getASTPath(); + List<TypePathEntry> tpes = new ArrayList<TypePathEntry>(); + if (ap1 == null) { + // || methodName == null && fieldName == null) + organized.add(ins); + continue; + } + + // First find the relevant "top-level" insertion, if any. + // ap0: path to top-level type; ap1: path to local type + ASTRecord rec; + Tree.Kind kind; + Deque<ASTPath> astack = new ArrayDeque<ASTPath>(ap1.size()); + ASTPath ap0 = ap1; + do { + astack.push(ap0); + ap0 = ap0.getParentPath(); + } while (!ap0.isEmpty()); + do { + ap0 = astack.pop(); + kind = ap0.get(-1).getTreeKind(); + rec = new ASTRecord(cut, className, methodName, fieldName, ap0); + } while (!(astack.isEmpty() || map.containsKey(rec))); + + TypedInsertion tins = map.get(rec); + TreePath path = ASTIndex.getTreePath(cut, rec); + Tree node = path == null ? null : path.getLeaf(); + if (node == null && ap0.isEmpty()) { + organized.add(ins); + continue; + } + + // Try to create a top-level insertion if none exists (e.g., if + // there is an insertion for NewArray.type 1 but not for 0). + if (tins == null) { + GenericArrayLocationCriterion galc = + criteria.getGenericArrayLocation(); + if (node == null) { + // TODO: figure out from rec? + organized.add(ins); + continue; + } else { + Tree t = path.getLeaf(); + switch (t.getKind()) { + case NEW_ARRAY: + int d = 0; + ASTPath.ASTEntry e = ap1.get(-1); + List<TypePathEntry> loc = null; + List<Insertion> inners = new ArrayList<Insertion>(); + Type type = TypeTree.conv(((JCTree.JCNewArray) t).type); + if (e.getTreeKind() == Tree.Kind.NEW_ARRAY) { + d += e.getArgument(); + } + if (galc != null) { + loc = galc.getLocation(); + int n = loc.size(); + while (--n >= 0 && loc.get(n).tag == TypePathEntryKind.ARRAY) { + ++d; + } + loc = n < 0 ? null : loc.subList(0, ++n); + } + criteria.add(new ASTPathCriterion( + rec.astPath.getParentPath().extendNewArray(d))); + criteria.add(loc == null || loc.isEmpty() + ? new GenericArrayLocationCriterion() + : new GenericArrayLocationCriterion( + new InnerTypeLocation(loc))); + inners.add(ins); + tins = new NewInsertion(type, criteria, inners); + tins.setInserted(true); + map.put(rec, tins); + break; + default: + break; + } + path = path.getParentPath(); + } + } + + // The sought node may or may not be found in the tree; if not, it + // may need to be created later. Use whatever part of the path + // exists already to distinguish MEMBER_SELECT nodes that indicate + // qualifiers from those that indicate local types. Assume any + // MEMBER_SELECTs in the AST path that don't correspond to + // existing nodes are part of a type use. + if (node == null) { + ASTPath ap = ap0; + if (!ap.isEmpty()) { + do { + ap = ap.getParentPath(); + node = ASTIndex.getNode(cut, rec.replacePath(ap)); + } while (node == null && !ap.isEmpty()); + } + if (node == null) { + organized.add(ins); + continue; + } + + // find actual type + ClassSymbol csym = null; + switch (tins.getKind()) { + case CONSTRUCTOR: + if (node instanceof JCTree.JCMethodDecl) { + MethodSymbol msym = ((JCTree.JCMethodDecl) node).sym; + csym = (ClassSymbol) msym.owner; + node = TypeTree.fromType(csym.type); + break; + } else if (node instanceof JCTree.JCClassDecl) { + csym = ((JCTree.JCClassDecl) node).sym; + if (csym.owner instanceof ClassSymbol) { + csym = (ClassSymbol) csym.owner; + node = TypeTree.fromType(csym.type); + break; + } + } + throw new RuntimeException(); + case NEW: + if (node instanceof JCTree.JCNewArray) { + if (node.toString().startsWith("{")) { + node = TypeTree.fromType(((JCTree.JCNewArray) node).type); + break; + } else { + organized.add(ins); + continue; + } + } + throw new RuntimeException(); + case RECEIVER: + if (node instanceof JCTree.JCMethodDecl) { + JCTree.JCMethodDecl jmd = (JCTree.JCMethodDecl) node; + csym = (ClassSymbol) jmd.sym.owner; + if ("<init>".equals(jmd.name.toString())) { + csym = (ClassSymbol) csym.owner; + } + } else if (node instanceof JCTree.JCClassDecl) { + csym = ((JCTree.JCClassDecl) node).sym; + } + if (csym != null) { + node = TypeTree.fromType(csym.type); + break; + } + throw new RuntimeException(); + default: + throw new RuntimeException(); + } + } + + /* + * Inner types require special consideration due to the + * structural differences between an AST that represents a type + * (subclass of com.sun.source.Tree) and the type's logical + * representation (subclass of type.Type). The differences are + * most prominent in the case of a type with a parameterized + * local type. For example, the AST for A.B.C<D> looks like + * this: + * + * ParameterizedType + * / \ + * MemberSelect Identifier + * / \ | + * MemberSelect (Name) D + * / \ | + * Identifier (Name) C + * | | + * A B + * + * (Technically, the Names are not AST nodes but rather + * attributes of their parent MemberSelect nodes.) The logical + * representation seems more intuitive: + * + * DeclaredType + * / | \ + * Name Params Inner + * | | | + * A - DeclaredType + * / | \ + * Name Params Inner + * | | | + * B - DeclaredType + * / | \ + * Name Params Inner + * | | | + * C D - + * + * The opposing "chirality" of local type nesting means that the + * usual recursive descent strategy doesn't work for finding a + * logical type path in an AST; in effect, local types have to + * be "turned inside-out". + * + * Worse yet, the actual tree structure may not exist in the tree! + * It is possible to recover the actual type from the symbol + * table, but the methods to create AST nodes are not visible + * here. Hence, the conversion relies on custom implementations + * of the interfaces in com.sun.source.tree.Tree, which are + * defined in the local class TypeTree. + */ + int i = ap0.size(); + int n = ap1.size(); + int actualDepth = 0; // inner type levels seen + int expectedDepth = 0; // inner type levels anticipated + + // skip any declaration nodes + while (i < n) { + ASTPath.ASTEntry entry = ap1.get(i); + kind = entry.getTreeKind(); + if (kind != Tree.Kind.METHOD && kind != Tree.Kind.VARIABLE) { + break; + } + ++i; + } + + // now build up the type path in JVM's format + while (i < n) { + ASTPath.ASTEntry entry = ap1.get(i); + rec = rec.extend(entry); + kind = entry.getTreeKind(); + + while (node.getKind() == Tree.Kind.ANNOTATED_TYPE) { // skip + node = ((AnnotatedTypeTree) node).getUnderlyingType(); + } + if (expectedDepth == 0) { + expectedDepth = localDepth(node); + } + + switch (kind) { + case ARRAY_TYPE: + if (expectedDepth == 0 && node.getKind() == kind) { + node = ((ArrayTypeTree) node).getType(); + while (--actualDepth >= 0) { + tpes.add(TypePathEntry.INNER_TYPE); + } + tpes.add(TypePathEntry.ARRAY); + break; + } + throw new RuntimeException(); + + case MEMBER_SELECT: + if (--expectedDepth >= 0) { // otherwise, shouldn't have MEMBER_SELECT + node = ((MemberSelectTree) node).getExpression(); + ++actualDepth; + break; + } + throw new RuntimeException(); + + case NEW_ARRAY: + assert tpes.isEmpty(); + ap0 = ap0.add(new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, + ASTPath.TYPE, 0)); + if (expectedDepth == 0 && node.getKind() == kind) { + if (node instanceof JCTree.JCNewArray) { + int arg = entry.getArgument(); + if (arg > 0) { + node = ((JCTree.JCNewArray) node).elemtype; + tpes.add(TypePathEntry.ARRAY); + while (--arg > 0 && node instanceof JCTree.JCArrayTypeTree) { + node = ((JCTree.JCArrayTypeTree) node).elemtype; + tpes.add(TypePathEntry.ARRAY); + } + if (arg > 0) { throw new RuntimeException(); } + } else { + node = TypeTree.fromType(((JCTree.JCNewArray) node).type); + } + } else { + throw new RuntimeException("NYI"); // TODO + } + break; + } + throw new RuntimeException(); + + case PARAMETERIZED_TYPE: + if (node.getKind() == kind) { + ParameterizedTypeTree ptt = (ParameterizedTypeTree) node; + if (entry.childSelectorIs(ASTPath.TYPE)) { + node = ptt.getType(); + break; // ParameterizedType.type is "transparent" wrt type path + } else if (expectedDepth == 0 + && entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) { + List<? extends Tree> typeArgs = ptt.getTypeArguments(); + int j = entry.getArgument(); + if (j >= 0 && j < typeArgs.size()) { + // make sure any inner types are accounted for + actualDepth = 0; + expectedDepth = localDepth(ptt.getType()); + while (--expectedDepth >= 0) { + tpes.add(TypePathEntry.INNER_TYPE); + } + node = typeArgs.get(j); + tpes.add( + new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, j)); + break; + } + } + } + throw new RuntimeException(); + + case UNBOUNDED_WILDCARD: + if (ASTPath.isWildcard(node.getKind())) { + if (expectedDepth == 0 + && (i < 1 + || ap1.get(i-1).getTreeKind() != Tree.Kind.INSTANCE_OF) + && (i < 2 + || ap1.get(i-2).getTreeKind() != Tree.Kind.ARRAY_TYPE)) { + while (--actualDepth >= 0) { + tpes.add(TypePathEntry.INNER_TYPE); + } + tpes.add(TypePathEntry.WILDCARD); + break; + } + } + throw new RuntimeException(); + + default: + node = ASTIndex.getNode(cut, rec); + break; + } + + ++i; + } + + while (--actualDepth >= 0) { + tpes.add(TypePathEntry.INNER_TYPE); + } + + organized.add(ins); + if (tpes.isEmpty()) { + // assert ap1.equals(ap0) && !map.containsKey(ap0); +// organized.add(ins); + // map.put(rec, (TypedInsertion) ins); + } else { + criteria.add(new ASTPathCriterion(ap0)); + criteria.add(new GenericArrayLocationCriterion( + new InnerTypeLocation(tpes))); + tins.getInnerTypeInsertions().add(ins); + } + } + organized.addAll(map.values()); + return organized; + } + + private int newArrayInnerTypeDepth(ASTPath path) { + int d = 0; + if (path != null) { + while (!path.isEmpty()) { + ASTPath.ASTEntry entry = path.get(-1); + switch (entry.getTreeKind()) { + case ANNOTATED_TYPE: + case MEMBER_SELECT: + case PARAMETERIZED_TYPE: + case UNBOUNDED_WILDCARD: + d = 0; + break; + case ARRAY_TYPE: + ++d; + break; + case NEW_ARRAY: + if (entry.childSelectorIs(ASTPath.TYPE) && entry.hasArgument()) { + d += entry.getArgument(); + } + return d; + default: + return 0; + } + path = path.getParentPath(); + } + } + return 0; + } + + /** + * Find an {@link ASTRecord} for the tree corresponding to a nested + * type of the type (use) to which the given record corresponds. + * + * @param rec record of (outer) type AST to be annotated + * @param loc inner type path + * @return record that locates the (nested) type in the source + */ + private ASTRecord extendToInnerType(ASTRecord rec, List<TypePathEntry> loc) { + ASTRecord r = rec; + Iterator<TypePathEntry> iter = loc.iterator(); + int depth = 0; + + while (iter.hasNext()) { + TypePathEntry tpe = iter.next(); + switch (tpe.tag) { + case ARRAY: + while (depth-- > 0) { + r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION); + } + r = r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE); + break; + case INNER_TYPE: + ++depth; + break; + case TYPE_ARGUMENT: + depth = 0; + r = r.extend(Tree.Kind.PARAMETERIZED_TYPE, ASTPath.TYPE_ARGUMENT, + tpe.arg); + break; + case WILDCARD: + while (depth-- > 0) { + r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION); + } + r = r.extend(Tree.Kind.UNBOUNDED_WILDCARD, ASTPath.BOUND); + break; + default: + throw new RuntimeException(); + } + } + while (depth-- > 0) { + r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION); + } + return r; + } + + /** + * Find an {@link ASTRecord} for the tree corresponding to a nested + * type of the type (use) to which the given tree and record + * correspond. + * + * @param rec record that locates {@code node} in the source + * @param loc inner type path + * @param node starting point for inner type path + * @return record that locates the nested type in the source + */ + private ASTRecord extendToInnerType(ASTRecord rec, + List<TypePathEntry> loc, Tree node) { + ASTRecord r = rec; + Tree t = node; + Iterator<TypePathEntry> iter = loc.iterator(); + TypePathEntry tpe = iter.next(); + +outer: + while (true) { + int d = localDepth(node); + + switch (t.getKind()) { + case ANNOTATED_TYPE: + r = r.extend(Tree.Kind.ANNOTATED_TYPE, ASTPath.TYPE); + t = ((JCTree.JCAnnotatedType) t).getUnderlyingType(); + break; + + case ARRAY_TYPE: + if (d == 0 && tpe.tag == TypePathEntryKind.ARRAY) { + int a = 0; + if (!r.astPath.isEmpty()) { + ASTPath.ASTEntry e = r.astPath.get(-1); + if (e.getTreeKind() == Tree.Kind.NEW_ARRAY + && e.childSelectorIs(ASTPath.TYPE)) { + a = 1 + e.getArgument(); + } + } + r = a > 0 + ? r.replacePath(r.astPath.getParentPath()) + .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, a) + : r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE); + t = ((ArrayTypeTree) t).getType(); + break; + } + throw new RuntimeException(); + + case MEMBER_SELECT: + if (d > 0 && tpe.tag == TypePathEntryKind.INNER_TYPE) { + Tree temp = t; + do { + temp = ((JCTree.JCFieldAccess) temp).getExpression(); + if (!iter.hasNext()) { + do { + r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION); + } while (--d > 0); + return r; + } + tpe = iter.next(); + if (--d == 0) { + continue outer; // avoid next() at end of loop + } + } while (tpe.tag == TypePathEntryKind.INNER_TYPE); + } + throw new RuntimeException(); + + case NEW_ARRAY: + if (d == 0) { + if (!r.astPath.isEmpty()) { + ASTPath.ASTEntry e = r.astPath.get(-1); + if (e.getTreeKind() == Tree.Kind.NEW_ARRAY) { + int a = 0; + while (tpe.tag == TypePathEntryKind.ARRAY) { + ++a; + if (!iter.hasNext()) { break; } + tpe = iter.next(); + } + r = r.replacePath(r.astPath.getParentPath()) + .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, a); + break; + } + } + r = r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE); + t = ((JCTree.JCArrayTypeTree) t).getType(); + break; + } + throw new RuntimeException(); + + case PARAMETERIZED_TYPE: + if (d == 0 && tpe.tag == TypePathEntryKind.TYPE_ARGUMENT) { + r = r.extend(Tree.Kind.PARAMETERIZED_TYPE, + ASTPath.TYPE_ARGUMENT, tpe.arg); + t = ((JCTree.JCTypeApply) t).getTypeArguments().get(tpe.arg); + break; + } else if (d > 0 && tpe.tag == TypePathEntryKind.INNER_TYPE) { + Tree temp = ((JCTree.JCTypeApply) t).getType(); + r = r.extend(Tree.Kind.PARAMETERIZED_TYPE, ASTPath.TYPE); + t = temp; + do { + temp = ((JCTree.JCFieldAccess) temp).getExpression(); + if (!iter.hasNext()) { + do { + r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION); + } while (--d > 0); + return r; + } + tpe = iter.next(); + if (--d == 0) { + continue outer; // avoid next() at end of loop + } + } while (tpe.tag == TypePathEntryKind.INNER_TYPE); + } + throw new RuntimeException(); + + case EXTENDS_WILDCARD: + case SUPER_WILDCARD: + case UNBOUNDED_WILDCARD: + if (tpe.tag == TypePathEntryKind.WILDCARD) { + t = ((JCTree.JCWildcard) t).getBound(); + break; + } + throw new RuntimeException(); + + default: + if (iter.hasNext()) { + throw new RuntimeException(); + } + } + + if (!iter.hasNext()) { return r; } + tpe = iter.next(); + } + } + + // merge annotations, assuming types are structurally identical + private void mergeTypedInsertions(TypedInsertion ins0, TypedInsertion ins1) { + mergeTypes(ins0.getType(), ins1.getType()); + } + + private void mergeTypes(Type t0, Type t1) { + if (t0 == t1) { return; } + switch (t0.getKind()) { + case ARRAY: + { + ArrayType at0 = (ArrayType) t0; + ArrayType at1 = (ArrayType) t1; + mergeTypes(at0.getComponentType(), at1.getComponentType()); + return; + } + case BOUNDED: + { + BoundedType bt0 = (BoundedType) t0; + BoundedType bt1 = (BoundedType) t1; + if (bt0.getBoundKind() != bt1.getBoundKind()) { break; } + mergeTypes(bt0.getBound(), bt1.getBound()); + mergeTypes(bt0.getName(), bt1.getName()); + return; + } + case DECLARED: + { + DeclaredType dt0 = (DeclaredType) t0; + DeclaredType dt1 = (DeclaredType) t1; + List<Type> tps0 = dt0.getTypeParameters(); + List<Type> tps1 = dt1.getTypeParameters(); + int n = tps0.size(); + if (tps1.size() != n) { break; } + mergeTypes(dt0.getInnerType(), dt1.getInnerType()); + for (String anno : dt1.getAnnotations()) { + if (!dt0.getAnnotations().contains(anno)) { + dt0.addAnnotation(anno); + } + } + for (int i = 0; i < n; i++) { + mergeTypes(tps0.get(i), tps1.get(i)); + } + return; + } + } + throw new RuntimeException(); + } + + // Returns the depth of the innermost local type of a type AST. + private int localDepth(Tree node) { + Tree t = node; + int n = 0; +loop: + while (t != null) { + switch (t.getKind()) { + case ANNOTATED_TYPE: + t = ((AnnotatedTypeTree) t).getUnderlyingType(); + break; + case MEMBER_SELECT: + if (t instanceof JCTree.JCFieldAccess) { + JCTree.JCFieldAccess jfa = (JCTree.JCFieldAccess) t; + if (jfa.sym.kind == Kinds.PCK) { + t = jfa.getExpression(); + continue; + } + } + t = ((MemberSelectTree) t).getExpression(); + ++n; + break; + default: + break loop; + } + } + return n; + } + + // Provides an additional level of indexing. + class ASTRecordMap<E> implements Map<ASTRecord, E> { + Map<ASTRecord, SortedMap<ASTPath, E>> back; + + ASTRecordMap() { + back = new HashMap<ASTRecord, SortedMap<ASTPath, E>>(); + } + + private SortedMap<ASTPath, E> getMap(ASTRecord rec) { + ASTRecord key = rec.replacePath(ASTPath.empty()); + SortedMap<ASTPath, E> map = back.get(key); + if (map == null) { + map = new TreeMap<ASTPath, E>(); + back.put(key, map); + } + return map; + } + + @Override + public int size() { + int n = 0; + for (SortedMap<ASTPath, E> map : back.values()) { + n += map.size(); + } + return n; + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean containsKey(Object key) { + ASTRecord rec = (ASTRecord) key; + SortedMap<ASTPath, E> m = getMap(rec); + return m != null && m.containsKey(rec.astPath); + } + + @Override + public boolean containsValue(Object value) { + @SuppressWarnings("unchecked") + E e = (E) value; + for (SortedMap<ASTPath, E> map : back.values()) { + if (map.containsValue(e)) { return true; } + } + return false; + } + + @Override + public E get(Object key) { + ASTRecord rec = (ASTRecord) key; + SortedMap<ASTPath, E> map = getMap(rec); + return map == null ? null : map.get(rec.astPath); + } + + @Override + public E put(ASTRecord key, E value) { + ASTRecord rec = key; + SortedMap<ASTPath, E> map = getMap(rec); + return map == null ? null : map.put(rec.astPath, value); + } + + @Override + public E remove(Object key) { + ASTRecord rec = (ASTRecord) key; + SortedMap<ASTPath, E> map = getMap(rec); + return map == null ? null : map.remove(rec.astPath); + } + + @Override + public void putAll(Map<? extends ASTRecord, ? extends E> m) { + for (Map.Entry<? extends ASTRecord, ? extends E> entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + back.clear(); + } + + @Override + public Set<ASTRecord> keySet() { + return back.keySet(); + } + + @Override + public Collection<E> values() { + Set<E> ret = new LinkedHashSet<E>(); + for (SortedMap<ASTPath, E> m : back.values()) { + ret.addAll(m.values()); + } + return ret; + } + + @Override + public Set<Map.Entry<ASTRecord, E>> entrySet() { + final int size = size(); + return new AbstractSet<Map.Entry<ASTRecord, E>>() { + @Override + public Iterator<Map.Entry<ASTRecord, E>> iterator() { + return new Iterator<Map.Entry<ASTRecord, E>>() { + Iterator<Map.Entry<ASTRecord, SortedMap<ASTPath, E>>> iter0 = + back.entrySet().iterator(); + Iterator<Map.Entry<ASTPath, E>> iter1 = + Collections.<Map.Entry<ASTPath, E>>emptyIterator(); + ASTRecord rec = null; + + @Override + public boolean hasNext() { + if (iter1.hasNext()) { return true; } + while (iter0.hasNext()) { + Map.Entry<ASTRecord, SortedMap<ASTPath, E>> entry = + iter0.next(); + rec = entry.getKey(); + iter1 = entry.getValue().entrySet().iterator(); + if (iter1.hasNext()) { return true; } + } + iter1 = Collections.<Map.Entry<ASTPath, E>>emptyIterator(); + return false; + } + + @Override + public Map.Entry<ASTRecord, E> next() { + if (!hasNext()) { throw new NoSuchElementException(); } + final Map.Entry<ASTPath, E> e0 = iter1.next(); + return new Map.Entry<ASTRecord, E>() { + final ASTRecord key = rec.replacePath(e0.getKey()); + final E val = e0.getValue(); + @Override public ASTRecord getKey() { return key; } + @Override public E getValue() { return val; } + @Override public E setValue(E value) { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public int size() { return size; } + }; + } + } + + // Simple AST implementation used only in determining type paths. + static abstract class TypeTree implements ExpressionTree { + private static Map<String, TypeTag> primTags = + new HashMap<String, TypeTag>(); + { + primTags.put("byte", TypeTag.BYTE); + primTags.put("char", TypeTag.CHAR); + primTags.put("short", TypeTag.SHORT); + primTags.put("long", TypeTag.LONG); + primTags.put("float", TypeTag.FLOAT); + primTags.put("int", TypeTag.INT); + primTags.put("double", TypeTag.DOUBLE); + primTags.put("boolean", TypeTag.BOOLEAN); + } + + static TypeTree fromJCTree(JCTree jt) { + if (jt != null) { + Kind kind = jt.getKind(); + switch (kind) { + case ANNOTATED_TYPE: + return fromJCTree( + ((JCTree.JCAnnotatedType) jt).getUnderlyingType()); + case IDENTIFIER: + return new IdenT( + ((JCTree.JCIdent) jt).sym.getSimpleName().toString()); + case ARRAY_TYPE: + return new ArrT( + fromJCTree(((JCTree.JCArrayTypeTree) jt).getType())); + case MEMBER_SELECT: + return new LocT( + fromJCTree(((JCTree.JCFieldAccess) jt).getExpression()), + ((JCTree.JCFieldAccess) jt).getIdentifier()); + case EXTENDS_WILDCARD: + case SUPER_WILDCARD: + return new WildT(kind, + fromJCTree(((JCTree.JCWildcard) jt).getBound())); + case UNBOUNDED_WILDCARD: + return new WildT(); + case PARAMETERIZED_TYPE: + com.sun.tools.javac.util.List<JCExpression> typeArgs = + ((JCTree.JCTypeApply) jt).getTypeArguments(); + List<Tree> args = new ArrayList<Tree>(typeArgs.size()); + for (JCTree.JCExpression typeArg : typeArgs) { + args.add(fromJCTree(typeArg)); + } + return new ParT( + fromJCTree(((JCTree.JCTypeApply) jt).getType()), + args); + default: + break; + } + } + return null; + } + + static TypeTree fromType(final Type type) { + switch (type.getKind()) { + case ARRAY: + final ArrayType atype = (ArrayType) type; + final TypeTree componentType = fromType(atype.getComponentType()); + return new ArrT(componentType); + case BOUNDED: + final BoundedType btype = (BoundedType) type; + final BoundedType.BoundKind bk = btype.getBoundKind(); + final String bname = btype.getName().getName(); + final TypeTree bound = fromType(btype.getBound()); + return new Param(bname, bk, bound); + case DECLARED: + final DeclaredType dtype = (DeclaredType) type; + if (dtype.isWildcard()) { + return new WildT(); + } else { + final String dname = dtype.getName(); + TypeTag typeTag = primTags.get(dname); + if (typeTag == null) { + final TypeTree base = new IdenT(dname); + TypeTree ret = base; + List<Type> params = dtype.getTypeParameters(); + DeclaredType inner = dtype.getInnerType(); + if (!params.isEmpty()) { + final List<Tree> typeArgs = new ArrayList<Tree>(params.size()); + for (Type t : params) { typeArgs.add(fromType(t)); } + ret = new ParT(base, typeArgs); + } + return inner == null ? ret : meld(fromType(inner), ret); + } else { + final TypeKind typeKind = typeTag.getPrimitiveTypeKind(); + return new PrimT(typeKind); + } + } + default: + throw new RuntimeException("unknown type kind " + type.getKind()); + } + } + + static TypeTree fromType(final com.sun.tools.javac.code.Type type) { + return fromType(conv(type)); + } + + /** + * @param jtype + * @return + */ + static Type conv(final com.sun.tools.javac.code.Type jtype) { + Type type = null; + DeclaredType d; + com.sun.tools.javac.code.Type t; + switch (jtype.getKind()) { + case ARRAY: + t = ((com.sun.tools.javac.code.Type.ArrayType) jtype).elemtype; + type = new ArrayType(conv(t)); + break; + case DECLARED: + t = jtype; + d = null; + do { + DeclaredType d0 = d; + com.sun.tools.javac.code.Type.ClassType ct = + (com.sun.tools.javac.code.Type.ClassType) t; + d = new DeclaredType(ct.tsym.name.toString()); + d.setInnerType(d0); + d0 = d; + for (com.sun.tools.javac.code.Type a : ct.getTypeArguments()) { + d.addTypeParameter(conv(a)); + } + t = ct.getEnclosingType(); + } while (t.getKind() == TypeKind.DECLARED); + type = d; + break; + case WILDCARD: + BoundedType.BoundKind k; + t = ((com.sun.tools.javac.code.Type.WildcardType) jtype).bound; + switch (((com.sun.tools.javac.code.Type.WildcardType) jtype).kind) { + case EXTENDS: + k = BoundedType.BoundKind.EXTENDS; + break; + case SUPER: + k = BoundedType.BoundKind.SUPER; + break; + case UNBOUND: + k = null; + type = new DeclaredType("?"); + break; + default: + throw new RuntimeException(); + } + if (k != null) { + d = new DeclaredType(jtype.tsym.name.toString()); + type = new BoundedType(d, k, (DeclaredType) conv(t)); + } + break; + case TYPEVAR: + t = ((com.sun.tools.javac.code.Type.TypeVar) jtype).getUpperBound(); + type = conv(t); + if (type.getKind() == Type.Kind.DECLARED) { + type = new BoundedType(new DeclaredType(jtype.tsym.name.toString()), + BoundedType.BoundKind.EXTENDS, (DeclaredType) type); + } // otherwise previous conv should have been here already + break; + case INTERSECTION: + t = jtype.tsym.erasure_field; // ??? + type = new DeclaredType(t.tsym.name.toString()); + break; + case UNION: + // TODO + break; + case BOOLEAN: + case BYTE: + case CHAR: + case DOUBLE: + case LONG: + case SHORT: + case FLOAT: + case INT: + type = new DeclaredType(jtype.tsym.name.toString()); + break; + // case ERROR: + // case EXECUTABLE: + // case NONE: + // case NULL: + // case OTHER: + // case PACKAGE: + // case VOID: + default: + break; + } + return type; + } + + private static TypeTree meld(final TypeTree t0, final TypeTree t1) { + switch (t0.getKind()) { + case IDENTIFIER: + IdenT it = (IdenT) t0; + return new LocT(t1, it.getName()); + case MEMBER_SELECT: + LocT lt = (LocT) t0; + return new LocT(meld(lt.getExpression(), t1), lt.getIdentifier()); + case PARAMETERIZED_TYPE: + ParT pt = (ParT) t0; + return new ParT(meld(pt.getType(), t1), pt.getTypeArguments()); + default: + throw new IllegalArgumentException("unexpected type " + t0); + } + } + + static final class ArrT extends TypeTree implements ArrayTypeTree { + private final TypeTree componentType; + + ArrT(TypeTree componentType) { + this.componentType = componentType; + } + + @Override + public Kind getKind() { return Kind.ARRAY_TYPE; } + + @Override + public <R, D> R accept(TreeVisitor<R, D> visitor, D data) { + return visitor.visitArrayType(this, data); + } + + @Override + public TypeTree getType() { return componentType; } + + @Override + public String toString() { return componentType + "[]"; } + } + + static final class LocT extends TypeTree implements MemberSelectTree { + private final TypeTree expr; + private final Name name; + + LocT(TypeTree expr, Name name) { + this.expr = expr; + this.name = name; + } + + @Override + public Kind getKind() { return Kind.MEMBER_SELECT; } + + @Override + public <R, D> R accept(TreeVisitor<R, D> visitor, D data) { + return visitor.visitMemberSelect(this, data); + } + + @Override + public TypeTree getExpression() { return expr; } + + @Override + public Name getIdentifier() { return name; } + + @Override + public String toString() { return expr + "." + name; } + } + + static final class ParT extends TypeTree implements ParameterizedTypeTree { + private final TypeTree base; + private final List<? extends Tree> typeArgs; + + ParT(TypeTree base, List<? extends Tree> typeArgs) { + this.base = base; + this.typeArgs = typeArgs; + } + + @Override + public Kind getKind() { return Kind.PARAMETERIZED_TYPE; } + + @Override + public <R, D> R accept(TreeVisitor<R, D> visitor, D data) { + return visitor.visitParameterizedType(this, data); + } + + @Override + public TypeTree getType() { return base; } + + @Override + public List<? extends Tree> getTypeArguments() { + return typeArgs; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(base.toString()); + String s = "<"; + for (Tree t : typeArgs) { + sb.append(s); + sb.append(t.toString()); + s = ", "; + } + sb.append('>'); + return sb.toString(); + } + } + + static final class PrimT extends TypeTree implements PrimitiveTypeTree { + private final TypeKind typeKind; + + PrimT(TypeKind typeKind) { + this.typeKind = typeKind; + } + + @Override + public Kind getKind() { return Kind.PRIMITIVE_TYPE; } + + @Override + public <R, D> R accept(TreeVisitor<R, D> visitor, D data) { + return visitor.visitPrimitiveType(this, data); + } + + @Override + public TypeKind getPrimitiveTypeKind() { return typeKind; } + + @Override + public String toString() { + switch (typeKind) { + case BOOLEAN: return "boolean"; + case BYTE: return "byte"; + case CHAR: return "char"; + case DOUBLE: return "double"; + case FLOAT: return "float"; + case INT: return "int"; + case LONG: return "long"; + case SHORT: return "short"; + // case VOID: return "void"; + // case WILDCARD: return "?"; + default: + throw new IllegalArgumentException("unexpected type kind " + + typeKind); + } + } + } + + static final class IdenT extends TypeTree implements IdentifierTree { + private final String name; + + IdenT(String dname) { + this.name = dname; + } + + @Override + public Kind getKind() { return Kind.IDENTIFIER; } + + @Override + public <R, D> R accept(TreeVisitor<R, D> visitor, D data) { + return visitor.visitIdentifier(this, data); + } + + @Override + public Name getName() { return new TypeName(name); } + + @Override + public String toString() { return name; } + } + + static final class WildT extends TypeTree implements WildcardTree { + private final TypeTree bound; + private final Kind kind; + + WildT() { + this(Kind.UNBOUNDED_WILDCARD, null); + } + + WildT(TypeTree bound, BoundedType.BoundKind bk) { + this(bk == BoundedType.BoundKind.SUPER + ? Kind.SUPER_WILDCARD + : Kind.EXTENDS_WILDCARD, + bound); + } + + WildT(Kind kind, TypeTree bound) { + this.kind = kind; + this.bound = bound; + } + + @Override + public Kind getKind() { return kind; } + + @Override + public <R, D> R accept(TreeVisitor<R, D> visitor, D data) { + return visitor.visitWildcard(this, data); + } + + @Override + public Tree getBound() { return bound; } + + @Override + public String toString() { return "?"; } + } + + static final class Param extends TypeTree implements TypeParameterTree { + private final String bname; + private final BoundedType.BoundKind bk; + private final Tree bound; + + Param(String bname, BoundedType.BoundKind bk, TypeTree bound) { + this.bname = bname; + this.bk = bk; + this.bound = bound; + } + + @Override + public Kind getKind() { return Kind.TYPE_PARAMETER; } + + @Override + public <R, D> R accept(TreeVisitor<R, D> visitor, D data) { + return visitor.visitTypeParameter(this, data); + } + + @Override + public Name getName() { return new TypeName(bname); } + + @Override + public List<? extends Tree> getBounds() { + return Collections.singletonList(bound); + } + + @Override + public List<? extends AnnotationTree> getAnnotations() { + return Collections.emptyList(); + } + + @Override + public String toString() { + return bname + " " + bk.toString() + " " + bound.toString(); + } + } + + static final class TypeName implements Name { + private final String str; + + TypeName(String str) { + this.str = str; + } + + @Override + public int length() { return str.length(); } + + @Override + public char charAt(int index) { return str.charAt(index); } + + @Override + public CharSequence subSequence(int start, int end) { + return str.subSequence(start, end); + } + + @Override + public boolean contentEquals(CharSequence cs) { + if (cs != null) { + int n = length(); + if (cs.length() == n) { + for (int i = 0; i < n; i++) { + if (charAt(i) != cs.charAt(i)) { return false; } + } + return true; + } + } + return false; + } + + @Override + public String toString() { return str; } + } + } +}
diff --git a/annotation-file-utilities/src/annotator/find/InstanceOfCriterion.java b/annotation-file-utilities/src/annotator/find/InstanceOfCriterion.java new file mode 100644 index 0000000..2ed3ab9 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/InstanceOfCriterion.java
@@ -0,0 +1,92 @@ +package annotator.find; + +import annotations.el.RelativeLocation; +import annotator.scanner.InstanceOfScanner; + +import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class InstanceOfCriterion implements Criterion { + + private final String methodName; + private final RelativeLocation loc; + + public InstanceOfCriterion(String methodName, RelativeLocation loc) { + this.methodName = methodName.substring(0, methodName.lastIndexOf(")") + 1); + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + Criteria.dbug.debug("return null"); + return false; + } + + Tree leaf = path.getLeaf(); + + Criteria.dbug.debug("%n%s%n", this.toString()); + Criteria.dbug.debug("InstanceOfCriterion.isSatisfiedBy: %s%n", leaf); + Criteria.dbug.debug("leaf: %s%n", leaf); + Criteria.dbug.debug("kind: %s%n", leaf.getKind()); + Criteria.dbug.debug("class: %s%n", leaf.getClass()); + + TreePath parentPath = path.getParentPath(); + if (parentPath == null) { + Criteria.dbug.debug("return: parent path null%n"); + return false; + } + + Tree parent = parentPath.getLeaf(); + if (parent == null) { + Criteria.dbug.debug("return: parent null%n"); + return false; + } + + if (parent.getKind() == Tree.Kind.INSTANCE_OF) { + InstanceOfTree instanceOfTree = (InstanceOfTree) parent; + if (leaf != instanceOfTree.getType()) { + Criteria.dbug.debug("return: not type part of instanceof%n"); + return false; + } + + int indexInSource = InstanceOfScanner.indexOfInstanceOfTree(path, parent); + Criteria.dbug.debug("return source: %d%n", indexInSource); + boolean b; + if (loc.isBytecodeOffset()) { + int indexInClass = InstanceOfScanner.getMethodInstanceOfIndex(methodName, loc.offset); + Criteria.dbug.debug("return class: %d%n", indexInClass); + b = (indexInSource == indexInClass); + } else { + b = (indexInSource == loc.index); + Criteria.dbug.debug("return loc.index: %d%n", loc.index); + } + Criteria.dbug.debug("return new: %b", b); + return b; + } else { + boolean b = this.isSatisfiedBy(path.getParentPath()); + Criteria.dbug.debug("return parent: %b%n", b); + return b; + } + } + + @Override + public Kind getKind() { + return Kind.INSTANCE_OF; + } + + @Override + public String toString() { + return "InstanceOfCriterion: in method: " + methodName + " location: " + loc; + } + +}
diff --git a/annotation-file-utilities/src/annotator/find/IntersectionTypeLocationCriterion.java b/annotation-file-utilities/src/annotator/find/IntersectionTypeLocationCriterion.java new file mode 100644 index 0000000..313a328 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/IntersectionTypeLocationCriterion.java
@@ -0,0 +1,61 @@ +package annotator.find; + +import java.util.List; + +import annotations.el.RelativeLocation; +import annotations.io.ASTPath; + +import com.sun.source.tree.IntersectionTypeTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * @author dan + */ +public class IntersectionTypeLocationCriterion implements Criterion { + private final int typeIndex; + + public IntersectionTypeLocationCriterion(RelativeLocation loc) { + typeIndex = loc.type_index; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + @Override + public boolean isSatisfiedBy(TreePath path) { + TreePath parentPath = path.getParentPath(); + if (parentPath != null) { + Tree parent = parentPath.getLeaf(); + if (parentPath != null + && parent.getKind() == Tree.Kind.INTERSECTION_TYPE) { + IntersectionTypeTree itt = (IntersectionTypeTree) parent; + List<? extends Tree> bounds = itt.getBounds(); + Tree leaf = path.getLeaf(); + if (typeIndex < bounds.size() + && leaf == bounds.get(typeIndex)) { + return true; + } + } + } + Tree.Kind kind = path.getLeaf().getKind(); + if (ASTPath.isTypeKind(kind) || kind == Tree.Kind.MEMBER_SELECT) { + return isSatisfiedBy(path.getParentPath()); + } + return false; + } + + @Override + public Kind getKind() { + return Kind.INTERSECT_LOCATION; + } + + @Override + public String toString() { + return "IntersectionTypeLocation: at type index: " + typeIndex; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/IsCriterion.java b/annotation-file-utilities/src/annotator/find/IsCriterion.java new file mode 100644 index 0000000..bcd48cb --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/IsCriterion.java
@@ -0,0 +1,72 @@ +package annotator.find; + +import annotator.scanner.CommonScanner; + +import com.sun.source.tree.*; +import com.sun.source.util.TreePath; + +/** + * Represents the criterion that a program element has a particular type and + * name. + */ +final class IsCriterion implements Criterion { + + private final Tree.Kind kind; + private final String name; + + IsCriterion(Tree.Kind kind, String name) { + this.kind = kind; + this.name = name; + } + + /** + * {@inheritDoc} + */ + @Override + public Kind getKind() { + return Kind.HAS_KIND; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + Tree tree = path.getLeaf(); + if (CommonScanner.hasClassKind(tree)) { + return InClassCriterion.isSatisfiedBy(path, name, /*exactMatch=*/ true); + } + if (tree.getKind() != kind) { + return false; + } + switch (tree.getKind()) { + case VARIABLE: + String varName = ((VariableTree)tree).getName().toString(); + return varName.equals(name); + case METHOD: + String methodName = ((MethodTree)tree).getName().toString(); + return methodName.equals(name); + // case CLASS: + // return InClassCriterion.isSatisfiedBy(path, name, /*exactMatch=*/ true); + default: + throw new Error("unknown tree kind " + kind); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "is " + kind.toString().toLowerCase() + " '" + name + "'"; + } + +}
diff --git a/annotation-file-utilities/src/annotator/find/IsSigMethodCriterion.java b/annotation-file-utilities/src/annotator/find/IsSigMethodCriterion.java new file mode 100644 index 0000000..62bf373 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/IsSigMethodCriterion.java
@@ -0,0 +1,544 @@ +package annotator.find; + +import annotator.Main; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import plume.UtilMDE; + +import com.sun.source.tree.AnnotatedTypeTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.ImportTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; + +public class IsSigMethodCriterion implements Criterion { + + // The context is used for determining the fully qualified name of methods. + private static class Context { + public final String packageName; + public final List<String> imports; + public Context(String packageName, List<String> imports) { + this.packageName = packageName; + this.imports = imports; + } + } + + private static final Map<CompilationUnitTree, Context> contextCache = new HashMap<CompilationUnitTree, Context>(); + + private final String fullMethodName; // really the full JVML signature, sans return type + private final String simpleMethodName; + // list of parameters in Java, not JVML format + private final List<String> fullyQualifiedParams; + // in Java, not JVML, format. may be "void" + private final String returnType; + + public IsSigMethodCriterion(String methodName) { + this.fullMethodName = methodName.substring(0, methodName.indexOf(")") + 1); + this.simpleMethodName = methodName.substring(0, methodName.indexOf("(")); +// this.fullyQualifiedParams = new ArrayList<String>(); +// for (String s : methodName.substring( +// methodName.indexOf("(") + 1, methodName.indexOf(")")).split(",")) { +// if (s.length() > 0) { +// fullyQualifiedParams.add(s); +// } +// } + this.fullyQualifiedParams = new ArrayList<String>(); + try { + parseParams( + methodName.substring(methodName.indexOf("(") + 1, + methodName.indexOf(")"))); + } catch (Exception e) { + throw new RuntimeException("Caught exception while parsing method: " + + methodName, e); + } + String returnTypeJvml = methodName.substring(methodName.indexOf(")") + 1); + this.returnType = (returnTypeJvml.equals("V") + ? "void" + : UtilMDE.fieldDescriptorToBinaryName(returnTypeJvml)); + } + + // params is in JVML format + private void parseParams(String params) { + while (params.length() != 0) { + // nextParam is in JVML format + String nextParam = readNext(params); + params = params.substring(nextParam.length()); + fullyQualifiedParams.add(UtilMDE.fieldDescriptorToBinaryName(nextParam)); + } + } + + // strip a JVML type off a string containing multiple concatenated JVML types + private String readNext(String restOfParams) { + String firstChar = restOfParams.substring(0, 1); + if (isPrimitiveLetter(firstChar)) { + return firstChar; + } else if (firstChar.equals("[")) { + return "[" + readNext(restOfParams.substring(1)); + } else if (firstChar.equals("L")) { + return "L" + restOfParams.substring(1, restOfParams.indexOf(";") + 1); + } else { + throw new RuntimeException("Unknown method params: " + fullMethodName + " with remainder: " + restOfParams); + } + } + + // called by isSatisfiedBy(TreePath), will get compilation unit on its own + private static Context initImports(TreePath path) { + CompilationUnitTree topLevel = path.getCompilationUnit(); + Context result = contextCache.get(topLevel); + if (result != null) { + return result; + } + + ExpressionTree packageTree = topLevel.getPackageName(); + String packageName; + if (packageTree == null) { + packageName = ""; // the default package + } else { + packageName = packageTree.toString(); + } + + List<String> imports = new ArrayList<String>(); + for (ImportTree i : topLevel.getImports()) { + String imported = i.getQualifiedIdentifier().toString(); + imports.add(imported); + } + + result = new Context(packageName, imports); + contextCache.put(topLevel, result); + return result; + } + + // Abstracts out the inner loop of matchTypeParams. + // goalType is fully-qualified. + private boolean matchTypeParam(String goalType, Tree type, + Map<String, String> typeToClassMap, + Context context) { + String simpleType = type.toString(); + + boolean haveMatch = matchSimpleType(goalType, simpleType, context); + if (!haveMatch) { + if (!typeToClassMap.isEmpty()) { + for (Map.Entry<String, String> p : typeToClassMap.entrySet()) { + simpleType = simpleType.replaceAll("\\b" + p.getKey() + "\\b", + p.getValue()); + haveMatch = matchSimpleType(goalType, simpleType, context); + if (!haveMatch) { + Criteria.dbug.debug("matchTypeParams() => false:%n"); + Criteria.dbug.debug(" type = %s%n", type); + Criteria.dbug.debug(" simpleType = %s%n", simpleType); + Criteria.dbug.debug(" goalType = %s%n", goalType); + } + } + } + } + return haveMatch; + } + + + private boolean matchTypeParams(List<? extends VariableTree> sourceParams, + Map<String, String> typeToClassMap, + Context context) { + assert sourceParams.size() == fullyQualifiedParams.size(); + for (int i = 0; i < sourceParams.size(); i++) { + String fullType = fullyQualifiedParams.get(i); + VariableTree vt = sourceParams.get(i); + Tree vtType = vt.getType(); + if (! matchTypeParam(fullType, vtType, typeToClassMap, context)) { + Criteria.dbug.debug( + "matchTypeParam() => false:%n i=%d vt = %s%n fullType = %s%n", + i, vt, fullType); + return false; + } + } + return true; + } + + + // simpleType is the name as it appeared in the source code. + // fullType is fully-qualified. + // Both are in Java, not JVML, format. + private boolean matchSimpleType(String fullType, String simpleType, Context context) { + Criteria.dbug.debug("matchSimpleType(%s, %s, %s)%n", + fullType, simpleType, context); + + // must strip off generics, is all of this necessary, though? + // do you ever have generics anywhere but at the end? + while (simpleType.contains("<")) { + int bracketIndex = simpleType.lastIndexOf("<"); + String beforeBracket = simpleType.substring(0, bracketIndex); + String afterBracket = simpleType.substring(simpleType.indexOf(">", bracketIndex) + 1); + simpleType = beforeBracket + afterBracket; + } + + + // TODO: arrays? + + // first try qualifying simpleType with this package name, + // then with java.lang + // then with default package + // then with all of the imports + + boolean matchable = false; + + if (!matchable) { + // match with this package name + String packagePrefix = context.packageName; + if (packagePrefix.length() > 0) { + packagePrefix = packagePrefix + "."; + } + if (matchWithPrefix(fullType, simpleType, packagePrefix)) { + matchable = true; + } + } + + if (!matchable) { + // match with java.lang + if (matchWithPrefix(fullType, simpleType, "java.lang.")) { + matchable = true; + } + } + + if (!matchable) { + // match with default package + if (matchWithPrefix(fullType, simpleType, "")) { + matchable = true; + } + } + + /* + * From Java 7 language definition 6.5.5.2 (Qualified Types): + * If a type name is of the form Q.Id, then Q must be either a type + * name or a package name. If Id names exactly one accessible type + * that is a member of the type or package denoted by Q, then the + * qualified type name denotes that type. + */ + if (!matchable) { + // match with any of the imports + for (String someImport : context.imports) { + String importPrefix = null; + if (someImport.contains("*")) { + // don't include the * in the prefix, should end in . + // TODO: this is a real bug due to nonnull, though I discovered it manually + // importPrefix = someImport.substring(0, importPrefix.indexOf("*")); + importPrefix = someImport.substring(0, someImport.indexOf("*")); + } else { + // if you imported a specific class, you can only use that import + // if the last part matches the simple type + String importSimpleType = + someImport.substring(someImport.lastIndexOf(".") + 1); + + // Remove array brackets from simpleType if it has them + int arrayBracket = simpleType.indexOf('['); + String simpleBaseType = simpleType; + if (arrayBracket > -1) { + simpleBaseType = simpleType.substring(0, arrayBracket); + } + if (!(simpleBaseType.equals(importSimpleType) + || simpleBaseType.startsWith(importSimpleType + "."))) { + continue; + } + + importPrefix = someImport.substring(0, someImport.lastIndexOf(".") + 1); + } + + if (matchWithPrefix(fullType, simpleType, importPrefix)) { + matchable = true; + break; // out of for loop + } + } + } + + return matchable; + } + + private boolean matchWithPrefix(String fullType, String simpleType, String prefix) { + return matchWithPrefixOneWay(fullType, simpleType, prefix) + || matchWithPrefixOneWay(simpleType, fullType, prefix); + } + + // simpleType can be in JVML format ?? Is that really possible? + private boolean matchWithPrefixOneWay(String fullType, String simpleType, + String prefix) { + + // maybe simpleType is in JVML format + String simpleType2 = simpleType.replace("/", "."); + + String fullType2 = fullType.replace("$", "."); + + /* unused String prefix2 = (prefix.endsWith(".") + ? prefix.substring(0, prefix.length() - 1) + : prefix); */ + boolean b = (fullType2.equals(prefix + simpleType2) + // Hacky way to handle the possibility that fulltype is an + // inner type but simple type is unqualified. + || (fullType.startsWith(prefix) + && (fullType.endsWith("$" + simpleType2) + || fullType2.endsWith("." + simpleType2)))); + Criteria.dbug.debug("matchWithPrefix(%s, %s, %s) => %b)%n", + fullType2, simpleType, prefix, b); + return b; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Context context = initImports(path); + + Tree leaf = path.getLeaf(); + + if (leaf.getKind() != Tree.Kind.METHOD) { + Criteria.dbug.debug( + "IsSigMethodCriterion.isSatisfiedBy(%s) => false: not a METHOD tree%n", + Main.pathToString(path)); + return false; + } + // else if ((((JCMethodDecl) leaf).mods.flags & Flags.GENERATEDCONSTR) != 0) { + // Criteria.dbug.debug( + // "IsSigMethodCriterion.isSatisfiedBy(%s) => false: generated constructor%n", + // Main.pathToString(path)); + // return false; + // } + + MethodTree mt = (MethodTree) leaf; + + if (! simpleMethodName.equals(mt.getName().toString())) { + Criteria.dbug.debug("IsSigMethodCriterion.isSatisfiedBy => false: Names don't match%n"); + return false; + } + + List<? extends VariableTree> sourceParams = mt.getParameters(); + if (fullyQualifiedParams.size() != sourceParams.size()) { + Criteria.dbug.debug("IsSigMethodCriterion.isSatisfiedBy => false: Number of parameters don't match%n"); + return false; + } + + // now go through all type parameters declared by method + // and for each one, create a mapping from the type to the + // first declared extended class, defaulting to Object + // for example, + // <T extends Date> void foo(T t) + // creates mapping: T -> Date + // <T extends Date & List> void foo(Object o) + // creates mapping: T -> Date + // <T extends Date, U extends List> foo(Object o) + // creates mappings: T -> Date, U -> List + // <T> void foo(T t) + // creates mapping: T -> Object + + Map<String, String> typeToClassMap = new HashMap<String, String>(); + for (TypeParameterTree param : mt.getTypeParameters()) { + String paramName = param.getName().toString(); + String paramClass = "Object"; + List<? extends Tree> paramBounds = param.getBounds(); + if (paramBounds != null && paramBounds.size() >= 1) { + Tree boundZero = paramBounds.get(0); + if (boundZero.getKind() == Tree.Kind.ANNOTATED_TYPE) { + boundZero = ((AnnotatedTypeTree) boundZero).getUnderlyingType(); + } + paramClass = boundZero.toString(); + } + typeToClassMap.put(paramName, paramClass); + } + + // Do the same for the enclosing class. + // The type variable might not be from the directly enclosing + // class, but from a further up class. + // Go through all enclosing classes and add the type parameters. + { + TreePath classpath = path; + ClassTree ct = enclosingClass(classpath); + while (ct!=null) { + for (TypeParameterTree param : ct.getTypeParameters()) { + String paramName = param.getName().toString(); + String paramClass = "Object"; + List<? extends Tree> paramBounds = param.getBounds(); + if (paramBounds != null && paramBounds.size() >= 1) { + Tree pb = paramBounds.get(0); + if (pb.getKind() == Tree.Kind.ANNOTATED_TYPE) { + pb = ((AnnotatedTypeTree)pb).getUnderlyingType(); + } + paramClass = pb.toString(); + } + typeToClassMap.put(paramName, paramClass); + } + classpath = classpath.getParentPath(); + ct = enclosingClass(classpath); + } + } + + if (! matchTypeParams(sourceParams, typeToClassMap, context)) { + Criteria.dbug.debug("IsSigMethodCriterion => false: Parameter types don't match%n"); + return false; + } + + if ((mt.getReturnType() != null) // must be a constructor + && (! matchTypeParam(returnType, mt.getReturnType(), typeToClassMap, context))) { + Criteria.dbug.debug("IsSigMethodCriterion => false: Return types don't match%n"); + return false; + } + + Criteria.dbug.debug("IsSigMethodCriterion.isSatisfiedBy => true%n"); + return true; + } + + /* This is a copy of the method from the Checker Framework + * TreeUtils.enclosingClass. + * We cannot have a dependency on the Checker Framework. + * TODO: as is the case there, anonymous classes are not handled correctly. + */ + private static ClassTree enclosingClass(final TreePath path) { + final Set<Tree.Kind> kinds = EnumSet.of( + Tree.Kind.CLASS, + Tree.Kind.ENUM, + Tree.Kind.INTERFACE, + Tree.Kind.ANNOTATION_TYPE + ); + TreePath p = path; + + while (p != null) { + Tree leaf = p.getLeaf(); + assert leaf != null; /*nninvariant*/ + if (kinds.contains(leaf.getKind())) { + return (ClassTree) leaf; + } + p = p.getParentPath(); + } + + return null; + } + + @Override + public Kind getKind() { + return Kind.SIG_METHOD; + } + +// public static String getSignature(MethodTree mt) { +// String sig = mt.getName().toString().trim(); // method name, no parameters +// sig += "("; +// boolean first = true; +// for (VariableTree vt : mt.getParameters()) { +// if (!first) { +// sig += ","; +// } +// sig += getType(vt.getType()); +// first = false; +// } +// sig += ")"; +// +// return sig; +// } +// +// private static String getType(Tree t) { +// if (t.getKind() == Tree.Kind.PRIMITIVE_TYPE) { +// return getPrimitiveType((PrimitiveTypeTree) t); +// } else if (t.getKind() == Tree.Kind.IDENTIFIER) { +// return "L" + ((IdentifierTree) t).getName().toString(); +// } else if (t.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { +// // don't care about generics due to erasure +// return getType(((ParameterizedTypeTree) t).getType()); +// } +// throw new RuntimeException("unable to get type of: " + t); +// } +// +// private static String getPrimitiveType(PrimitiveTypeTree pt) { +// TypeKind tk = pt.getPrimitiveTypeKind(); +// if (tk == TypeKind.ARRAY) { +// return "["; +// } else if (tk == TypeKind.BOOLEAN) { +// return "Z"; +// } else if (tk == TypeKind.BYTE) { +// return "B"; +// } else if (tk == TypeKind.CHAR) { +// return "C"; +// } else if (tk == TypeKind.DOUBLE) { +// return "D"; +// } else if (tk == TypeKind.FLOAT) { +// return "F"; +// } else if (tk == TypeKind.INT) { +// return "I"; +// } else if (tk == TypeKind.LONG) { +// return "J"; +// } else if (tk == TypeKind.SHORT) { +// return "S"; +// } +// +// throw new RuntimeException("Invalid TypeKind: " + tk); +// } + + /* + private boolean isPrimitive(String s) { + return + s.equals("boolean") || + s.equals("byte") || + s.equals("char") || + s.equals("double") || + s.equals("float") || + s.equals("int") || + s.equals("long") || + s.equals("short"); + } + */ + + private boolean isPrimitiveLetter(String s) { + return + s.equals("Z") || + s.equals("B") || + s.equals("C") || + s.equals("D") || + s.equals("F") || + s.equals("I") || + s.equals("J") || + s.equals("S"); + } + + /* + private String primitiveLetter(String s) { + if (s.equals("boolean")) { + return "Z"; + } else if (s.equals("byte")) { + return "B"; + } else if (s.equals("char")) { + return "C"; + } else if (s.equals("double")) { + return "D"; + } else if (s.equals("float")) { + return "F"; + } else if (s.equals("int")) { + return "I"; + } else if (s.equals("long")) { + return "J"; + } else if (s.equals("short")) { + return "S"; + } else { + throw new RuntimeException("IsSigMethodCriterion: unknown primitive: " + s); + } + } + */ + + @Override + public String toString() { + return "IsSigMethodCriterion: " + fullMethodName; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/LambdaCriterion.java b/annotation-file-utilities/src/annotator/find/LambdaCriterion.java new file mode 100644 index 0000000..a81ab70 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/LambdaCriterion.java
@@ -0,0 +1,86 @@ +package annotator.find; + +import annotations.el.RelativeLocation; +import annotator.scanner.LambdaScanner; + +import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class LambdaCriterion implements Criterion { + private final String methodName; + private final RelativeLocation loc; + + public LambdaCriterion(String methodName, RelativeLocation loc) { + this.methodName = methodName; + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + Criteria.dbug.debug("return null"); + return false; + } + + Tree leaf = path.getLeaf(); + + Criteria.dbug.debug("%n%s%n", this.toString()); + Criteria.dbug.debug("LambdaCriterion.isSatisfiedBy: %s%n", leaf); + Criteria.dbug.debug("leaf: %s%n", leaf); + Criteria.dbug.debug("kind: %s%n", leaf.getKind()); + Criteria.dbug.debug("class: %s%n", leaf.getClass()); + + TreePath parentPath = path.getParentPath(); + if (parentPath == null) { + Criteria.dbug.debug("return: parent path null%n"); + return false; + } + + Tree parent = parentPath.getLeaf(); + if (parent == null) { + Criteria.dbug.debug("return: parent null%n"); + return false; + } + + if (parent.getKind() == Tree.Kind.LAMBDA_EXPRESSION) { + // LambdaExpressionTree lambdaTree = (LambdaExpressionTree) parent; + + int indexInSource = LambdaScanner.indexOfLambdaExpressionTree(path, parent); + Criteria.dbug.debug("return source: %d%n", indexInSource); + boolean b; + if (loc.isBytecodeOffset()) { + int indexInClass = LambdaScanner.getMethodLambdaExpressionIndex(methodName, loc.offset); + Criteria.dbug.debug("return class: %d%n", indexInClass); + b = (indexInSource == indexInClass); + } else { + b = (indexInSource == loc.index); + Criteria.dbug.debug("return loc.index: %d%n", loc.index); + } + Criteria.dbug.debug("return new: %b%n", b); + return b; + } else { + boolean b = this.isSatisfiedBy(path.getParentPath()); + Criteria.dbug.debug("return parent: %b%n", b); + return b; + } + } + + @Override + public Kind getKind() { + return Kind.LAMBDA_EXPRESSION; + } + + @Override + public String toString() { + return "LambdaCriterion: at location: " + loc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/LocalVariableCriterion.java b/annotation-file-utilities/src/annotator/find/LocalVariableCriterion.java new file mode 100644 index 0000000..cfb6a9c --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/LocalVariableCriterion.java
@@ -0,0 +1,128 @@ +package annotator.find; + +import java.util.List; + +import annotations.el.LocalLocation; +import annotator.scanner.LocalVariableScanner; + +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.util.Pair; + +/** + * Criterion for being a specific local variable. + */ +public class LocalVariableCriterion implements Criterion { + + private final String fullMethodName; + private final LocalLocation loc; + + public LocalVariableCriterion(String methodName, LocalLocation loc) { + this.fullMethodName = methodName.substring(0, methodName.indexOf(")") + 1); + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + TreePath parentPath = path.getParentPath(); + if (parentPath != null) { + Tree parent = parentPath.getLeaf(); + if (parent != null) { + if ((parent instanceof VariableTree) + // Avoid matching formal parameters + && (! (parentPath.getParentPath().getLeaf() instanceof MethodTree))) { + VariableTree vtt = (VariableTree) parent; + String varName = vtt.getName().toString(); + + if (loc.varName!=null && loc.varName.equals(varName)) { + int varIndex = LocalVariableScanner.indexOfVarTree(path, vtt, varName); + + if (loc.varIndex==varIndex) { + // the location specifies a variable name and index and it matches the current variable + // -> hurray + return true; + } + return false; + } + + Pair<String, Pair<Integer, Integer>> key = + Pair.of(fullMethodName, Pair.of(loc.index, loc.scopeStart)); + String potentialVarName = + LocalVariableScanner.getFromMethodNameIndexMap(key); + if (potentialVarName != null) { + if (varName.equals(potentialVarName)) { + // now use methodNameCounter to ensure that if this is the + // i'th variable of this name, its offset is the i'th offset + // of all variables with this name + List<Integer> allOffsetsWithThisName = + LocalVariableScanner.getFromMethodNameCounter(fullMethodName, potentialVarName); +// methodNameCounter.get(fullMethodName).get(potentialVarName); + Integer thisVariablesOffset = + allOffsetsWithThisName.indexOf(loc.scopeStart); + + // now you need to make sure that this is the + // thisVariablesOffset'th variable tree in the entire source + int i = LocalVariableScanner.indexOfVarTree(path, parent, potentialVarName); + + if (i == thisVariablesOffset) { + return true; + } + } + } + } else { + // If present leaf does not yet satisfy the local variable + // criterion, note that it actually is the correct local variable + // if any of its parents satisfy this local variable criterion + // (and going all the way up past the top-level tree is taken + // care of by the check for null above. + // + // For example, if you have the tree for "Integer" + // for the local variable "List<Integer> foo;" + // the parent of the current leaf will satisfy the local variable + // criterion directly. The fact that you will never return true + // for something that is not the correct local variable comes + // from the fact that you can't contain one local variable + // within another. For example, you can't have + // List<Integer bar> foo; + // Thus, no local variable tree can contain another local + // variable tree. + // Another general example: + // List<Integer> foo = ...; + // If the tree for ... contains one local variable, there is no fear + // of a conflict with "List<Integer>", because "List<Integer> foo" + // is a subtree of "List<Integer> foo = ...;", so the two + // (possibly) conflicting local variable trees are both subtrees + // of the same tree, and neither is an ancestor of the other. + return this.isSatisfiedBy(parentPath); + // To do: should stop this once it gets to method? or some other top level? + } + } + } + return false; + } + + + @Override + public Kind getKind() { + return Kind.LOCAL_VARIABLE; + } + + @Override + public String toString() { + return "LocalVariableCriterion: in: " + fullMethodName + " loc: " + loc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/MemberReferenceCriterion.java b/annotation-file-utilities/src/annotator/find/MemberReferenceCriterion.java new file mode 100644 index 0000000..77f8ef3 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/MemberReferenceCriterion.java
@@ -0,0 +1,62 @@ +package annotator.find; + +import annotations.el.RelativeLocation; +import annotator.scanner.MemberReferenceScanner; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class MemberReferenceCriterion implements Criterion { + private final String methodName; + private final RelativeLocation loc; + + public MemberReferenceCriterion(String methodName, RelativeLocation loc) { + this.methodName = methodName; + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Tree leaf = path.getLeaf(); + + if (leaf.getKind() == Tree.Kind.MEMBER_REFERENCE) { + int indexInSource = + MemberReferenceScanner.indexOfMemberReferenceTree(path, leaf); + boolean b; + if (loc.isBytecodeOffset()) { + int indexInClass = + MemberReferenceScanner.getMemberReferenceIndex(methodName, + loc.offset); + b = (indexInSource == indexInClass); + } else { + b = (indexInSource == loc.index); + } + return b; + } else { + boolean b = this.isSatisfiedBy(path.getParentPath()); + return b; + } + } + + @Override + public Kind getKind() { + return Kind.METHOD_REFERENCE; + } + + @Override + public String toString() { + return "MemberReferenceCriterion: in method: " + methodName + " location: " + loc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/MethodBoundCriterion.java b/annotation-file-utilities/src/annotator/find/MethodBoundCriterion.java new file mode 100644 index 0000000..9be904b --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/MethodBoundCriterion.java
@@ -0,0 +1,45 @@ +package annotator.find; + +import annotations.el.BoundLocation; + +import com.sun.source.util.TreePath; +import com.sun.source.tree.Tree; + +public class MethodBoundCriterion implements Criterion { + + private final String methodName; + public final BoundLocation boundLoc; + private final Criterion sigMethodCriterion; + private final Criterion boundLocationCriterion; + + public MethodBoundCriterion(String methodName, BoundLocation boundLoc) { + this.methodName = methodName; + this.boundLoc = boundLoc; + this.sigMethodCriterion = Criteria.inMethod(methodName); + this.boundLocationCriterion = Criteria.atBoundLocation(boundLoc); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + return sigMethodCriterion.isSatisfiedBy(path) && + boundLocationCriterion.isSatisfiedBy(path); + } + + @Override + public Kind getKind() { + return Kind.METHOD_BOUND; + } + + @Override + public String toString() { + return "MethodBoundCriterion: method: " + methodName + " bound boundLoc: " + boundLoc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/NewCriterion.java b/annotation-file-utilities/src/annotator/find/NewCriterion.java new file mode 100644 index 0000000..6752f91 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/NewCriterion.java
@@ -0,0 +1,83 @@ +package annotator.find; + +import annotations.el.RelativeLocation; +import annotator.scanner.NewScanner; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * Criterion for being a specific object creation expression. + */ +public class NewCriterion implements Criterion { + + private final String methodName; + private final Criterion inMethodCriterion; + + private final RelativeLocation loc; + + public NewCriterion(String methodName, RelativeLocation loc) { + this.methodName = methodName.substring(0, methodName.lastIndexOf(")") + 1); + + if (!(methodName.startsWith("init for field") || + methodName.startsWith("static init number") + || methodName.startsWith("instance init number"))) { + // keep strings consistent with text used in IndexFileSpecification + this.inMethodCriterion = Criteria.inMethod(methodName); + } else { + this.inMethodCriterion = null; + } + + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { + return false; + } + + Tree leaf = path.getLeaf(); + + if (inMethodCriterion!=null && !inMethodCriterion.isSatisfiedBy(path)) { + // If we're not in the method now, the parent path may still be in the method. + // For example, the current leaf could be inside a method inside of an + // anonymous inner class defined in another method. + return this.isSatisfiedBy(path.getParentPath()); + } + if (leaf.getKind() == Tree.Kind.NEW_CLASS + || leaf.getKind() == Tree.Kind.NEW_ARRAY) { + int indexInSource = NewScanner.indexOfNewTree(path, leaf); + // System.out.printf("indexInSource=%d%n", indexInSource); + boolean b; + if (loc.isBytecodeOffset()) { + int indexInClass = NewScanner.getMethodNewIndex(methodName, loc.offset); + b = (indexInSource == indexInClass); + } else { + b = (indexInSource == loc.index); + } + return b; + } else { + return this.isSatisfiedBy(path.getParentPath()); + } + } + + @Override + public Kind getKind() { + return Kind.NEW; + } + + @Override + public String toString() { + return "NewCriterion in method: " + methodName + " at location " + loc; + } + +}
diff --git a/annotation-file-utilities/src/annotator/find/NewInsertion.java b/annotation-file-utilities/src/annotator/find/NewInsertion.java new file mode 100644 index 0000000..a7e4678 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/NewInsertion.java
@@ -0,0 +1,119 @@ +package annotator.find; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import type.DeclaredType; +import type.Type; + +/** + * @author dbro + * + */ +public class NewInsertion extends TypedInsertion { + private final static Pattern qualifiers = Pattern.compile("(?:\\w++\\.)*+"); + + /** + * If true, the type will be qualified with the name of the superclass. + */ + protected boolean qualifyType; + + /** + * Construct a NewInsertion. + * <p> + * If "new" already exists in the initializer, then pass a + * {@link DeclaredType} thats name is the empty String. This will only + * insert an annotation on the existing type. + * <p> + * To insert the annotation along with "new" and the type (for example, + * {@code @Anno new Type[] \{...\}}), set the name to the type to insert. + * This can be done either before calling this constructor, or by modifying + * the return value of {@link #getType()}. + * + * @param type the type to use when inserting the receiver + * @param criteria where to insert the text + * @param innerTypeInsertions the inner types to go on this receiver + */ + public NewInsertion(Type type, Criteria criteria, + List<Insertion> innerTypeInsertions) { + super(type, criteria, innerTypeInsertions); + annotationsOnly = false; + qualifyType = false; + } + + /** {@inheritDoc} */ + @Override + protected String getText(boolean comments, boolean abbreviate) { + if (annotationsOnly || type.getKind() != Type.Kind.ARRAY) { + StringBuilder b = new StringBuilder(); + List<String> annotations = type.getAnnotations(); + if (annotations.isEmpty()) { return ""; } + for (String a : annotations) { + b.append(' ').append(a); // initial space removed below + } + return new AnnotationInsertion(b.substring(1), getCriteria(), + getSeparateLine()).getText(comments, abbreviate); + } else { + DeclaredType baseType = getBaseType(); + boolean commentAnnotation = (comments && baseType.getName().isEmpty()); + String result = typeToString(type, commentAnnotation, abbreviate); + if (!baseType.getName().isEmpty()) { + // First, temporarily strip off any qualifiers. + Matcher matcher = qualifiers.matcher(result); + String prefix = ""; + if (matcher.find() && matcher.start() == 0) { + prefix = result.substring(0, matcher.end()); + result = result.substring(matcher.end()); + } + // If the variable name preceded the array brackets in the + // source, extract it from the result. + if (qualifyType) { + for (DeclaredType t = baseType; t != null; t = t.getInnerType()) { + result += t.getName() + "."; + } + } + // Finally, prepend extracted qualifiers. + result = prefix + result; + } + result = "new " + result; + if (comments) { + result = "/*>>> " + result + " */"; + } + return result; + } + } + + /** + * If {@code true}, qualify {@code type} with the name of the superclass. + * This will only happen if a "new" is inserted. + */ + public void setQualifyType(boolean qualifyType) { + this.qualifyType = qualifyType; + } + + /** {@inheritDoc} */ + @Override + protected boolean addLeadingSpace(boolean gotSeparateLine, int pos, + char precedingChar) { + if ((precedingChar == '.' || precedingChar == '(') + && getBaseType().getName().isEmpty()) { + // If only the annotation is being inserted then don't insert a + // space if it's immediately after a '.' or '(' + return false; + } + return super.addLeadingSpace(gotSeparateLine, pos, precedingChar); + } + + /** {@inheritDoc} */ + @Override + protected boolean addTrailingSpace(boolean gotSeparateLine) { + return true; + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.NEW; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/NotInMethodCriterion.java b/annotation-file-utilities/src/annotator/find/NotInMethodCriterion.java new file mode 100644 index 0000000..a0a0f38 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/NotInMethodCriterion.java
@@ -0,0 +1,54 @@ +package annotator.find; + +import annotations.io.ASTPath; + +import com.sun.source.tree.*; +import com.sun.source.util.TreePath; + +/** + * Represents the criterion that a program element is not enclosed by any + * method (i.e. it's a field, class type parameter, etc.). + */ +final class NotInMethodCriterion implements Criterion { + + /** + * {@inheritDoc} + */ + @Override + public Kind getKind() { + return Kind.NOT_IN_METHOD; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + do { + Tree.Kind kind = path.getLeaf().getKind(); + if (kind == Tree.Kind.METHOD) { + return false; + } + if (ASTPath.isClassEquiv(kind)) { + return true; + } + path = path.getParentPath(); + } while (path != null && path.getLeaf() != null); + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "not in method"; + } + +}
diff --git a/annotation-file-utilities/src/annotator/find/PackageCriterion.java b/annotation-file-utilities/src/annotator/find/PackageCriterion.java new file mode 100644 index 0000000..dd4f3be --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/PackageCriterion.java
@@ -0,0 +1,62 @@ +package annotator.find; + +import annotator.Main; + +import com.sun.source.tree.*; +import com.sun.source.util.TreePath; + +/** + * Represents the criterion that a program element is in a method with a + * certain name. + */ +final class PackageCriterion implements Criterion { + + private final String name; + + PackageCriterion(String name) { + this.name = name; + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.PACKAGE; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree tree) { + assert path == null || path.getLeaf() == tree; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + Tree tree = path.getLeaf(); + Criteria.dbug.debug("PackageCriterion.isSatisfiedBy(%s, %s); this=%s%n", + Main.pathToString(path), tree, this.toString()); + + if (tree.getKind() == Tree.Kind.COMPILATION_UNIT) { + CompilationUnitTree cu = (CompilationUnitTree)tree; + if (cu.getSourceFile().getName().endsWith("package-info.java")) { + ExpressionTree pn = cu.getPackageName(); + assert ((pn instanceof IdentifierTree) + || (pn instanceof MemberSelectTree)); + if (this.name.equals(pn.toString())) { + return true; + } + } + } + Criteria.dbug.debug("PackageCriterion.isSatisfiedBy => false%n"); + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "package '" + name + "'"; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/ParamCriterion.java b/annotation-file-utilities/src/annotator/find/ParamCriterion.java new file mode 100644 index 0000000..fea9022 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ParamCriterion.java
@@ -0,0 +1,71 @@ +package annotator.find; + +import java.util.List; + +import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; + +public class ParamCriterion implements Criterion { + + private final String methodName; + private final Integer paramPos; + + public ParamCriterion(String methodName, Integer pos) { + this.methodName = methodName.substring(0, methodName.indexOf(")") + 1); + this.paramPos = pos; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + + if (path == null) { + return false; + } + + // no inner type location, want to annotate outermost type + // i.e. @Nullable List list; + // @Nullable List<String> list; + Tree leaf = path.getLeaf(); + if (leaf instanceof VariableTree) { + Tree parent = path.getParentPath().getLeaf(); + List<? extends VariableTree> params; + switch (parent.getKind()) { + case METHOD: + params = ((MethodTree) parent).getParameters(); + break; + case LAMBDA_EXPRESSION: + params = ((LambdaExpressionTree) parent).getParameters(); + break; + default: + params = null; + break; + } + return params != null && params.size() > paramPos + && params.get(paramPos).equals(leaf); + } + + return this.isSatisfiedBy(path.getParentPath()); + } + + @Override + public Kind getKind() { + return Kind.PARAM; + } + + @Override + public String toString() { + return "ParamCriterion for method: " + methodName + " at position: " + + paramPos; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/ReceiverCriterion.java b/annotation-file-utilities/src/annotator/find/ReceiverCriterion.java new file mode 100644 index 0000000..a97db08 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ReceiverCriterion.java
@@ -0,0 +1,80 @@ +package annotator.find; + +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class ReceiverCriterion implements Criterion { + + private final String methodName; // no return type + private final Criterion isSigMethodCriterion; + + public ReceiverCriterion(String methodName) { + this.methodName = methodName; + isSigMethodCriterion = Criteria.isSigMethod(methodName); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + // want to annotate BlockTree returned by MethodTree.getBody(); + if (path == null) { + return false; + } + + if (path.getLeaf().getKind() == Tree.Kind.METHOD) { + if (isSigMethodCriterion.isSatisfiedBy(path)) { + MethodTree leaf = (MethodTree) path.getLeaf(); + // If the method already has a receiver, then insert directly on the + // receiver, not on the method. + return leaf.getReceiverParameter() == null; + } + return false; + } else { + // We may be attempting to insert an annotation on a type parameter of an + // existing receiver, so make sure this is the right receiver parameter: + // work up the tree to find the method declaration. Store the parameter we + // pass through up to the method declaration so we can make sure we came up + // through the receiver. Then check to make sure this is the correct method + // declaration. + Tree param = null; + TreePath parent = path; + while (parent != null && parent.getLeaf().getKind() != Tree.Kind.METHOD) { + if (parent.getLeaf().getKind() == Tree.Kind.VARIABLE) { + if (param == null) { + param = parent.getLeaf(); + } else { + // The only variable we should pass through is the receiver parameter. + // If we pass through more than one then this isn't the right place. + return false; + } + } + parent = parent.getParentPath(); + } + if (parent != null && param != null) { + MethodTree method = (MethodTree) parent.getLeaf(); + if (param == method.getReceiverParameter()) { + return isSigMethodCriterion.isSatisfiedBy(parent); + } + } + return false; + } + } + + @Override + public Kind getKind() { + return Kind.RECEIVER; + } + + @Override + public String toString() { + return "ReceiverCriterion for method: " + methodName; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/ReceiverInsertion.java b/annotation-file-utilities/src/annotator/find/ReceiverInsertion.java new file mode 100644 index 0000000..5c31488 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ReceiverInsertion.java
@@ -0,0 +1,138 @@ +package annotator.find; + +import java.util.List; + +import type.DeclaredType; + +/** + * An insertion for a method receiver. This supports inserting an + * annotation on an existing receiver and creating a new (annotated) + * receiver if none are present. + */ +public class ReceiverInsertion extends TypedInsertion { + /** + * If true a comma will be added at the end of the insertion (only if also + * inserting the receiver). + */ + private boolean addComma; + + /** + * If true, {@code this} will be qualified with the name of the + * superclass. + */ + private boolean qualifyThis; + + /** + * Construct a ReceiverInsertion. + * <p> + * If the receiver parameter already exists in the method declaration, then + * pass a DeclaredType thats name is the empty String. This will only insert + * an annotation on the existing receiver. + * <p> + * To insert the annotation and the receiver (for example, + * {@code @Anno Type this}) the name should be set to the type to insert. + * This can either be done before calling this constructor, or by modifying + * the return value of {@link #getType()}. + * <p> + * A comma will not be added to the end of the receiver. In the case that + * there is a parameter following the inserted receiver pass {@code true} to + * {@link #setAddComma(boolean)} to add a comma to the end of the receiver. + * + * @param type the type to use when inserting the receiver + * @param criteria where to insert the text + * @param innerTypeInsertions the inner types to go on this receiver + */ + public ReceiverInsertion(DeclaredType type, Criteria criteria, List<Insertion> innerTypeInsertions) { + super(type, criteria, innerTypeInsertions); + addComma = false; + qualifyThis = false; + } + + /** + * If {@code true} a comma will be added at the end of the receiver. + * This will only happen if a receiver is inserted (see + * {@link #ReceiverInsertion(DeclaredType, Criteria, List)} for a description of + * when a receiver is inserted). This is useful if the method already has + * one or more parameters. + */ + public void setAddComma(boolean addComma) { + this.addComma = addComma; + } + + /** + * If {@code true}, qualify {@code this} with the name of the superclass. + * This will only happen if a receiver is inserted (see + * {@link #ReceiverInsertion(DeclaredType, Criteria, List)} + * for a description of when a receiver is inserted). This is useful + * for inner class constructors. + */ + public void setQualifyType(boolean qualifyThis) { + this.qualifyThis = qualifyThis; + } + + /** {@inheritDoc} */ + @Override + protected String getText(boolean comments, boolean abbreviate) { + if (annotationsOnly) { + StringBuilder b = new StringBuilder(); + List<String> annotations = type.getAnnotations(); + if (annotations.isEmpty()) { return ""; } + for (String a : annotations) { + b.append(a); + b.append(' '); + } + return new AnnotationInsertion(b.toString(), getCriteria(), + getSeparateLine()).getText(comments, abbreviate); + } else { + DeclaredType baseType = getBaseType(); + boolean commentAnnotation = (comments && baseType.getName().isEmpty()); + String result = typeToString(type, commentAnnotation, abbreviate); + if (!baseType.getName().isEmpty()) { + result += " "; + if (qualifyThis) { + for (DeclaredType t = baseType; t != null; + t = t.getInnerType()) { + result += t.getName() + "."; + } + } + result += "this"; + if (addComma) { + result += ","; + } + if (comments) { + result = "/*>>> " + result + " */"; + } + } + return result; + } + } + + /** {@inheritDoc} */ + @Override + protected boolean addLeadingSpace(boolean gotSeparateLine, int pos, + char precedingChar) { + if (precedingChar == '.' && getBaseType().getName().isEmpty()) { + // If only the annotation is being inserted then don't insert a + // space if it's immediately after a '.' + return false; + } + return super.addLeadingSpace(gotSeparateLine, pos, precedingChar); + } + + /** {@inheritDoc} */ + @Override + protected boolean addTrailingSpace(boolean gotSeparateLine) { + // If the type is not already in the source and the receiver is the only + // parameter, don't add a trailing space. + if (!getBaseType().getName().isEmpty() && !addComma) { + return false; + } + return super.addTrailingSpace(gotSeparateLine); + } + + /** {@inheritDoc} */ + @Override + public Kind getKind() { + return Kind.RECEIVER; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/ReturnTypeCriterion.java b/annotation-file-utilities/src/annotator/find/ReturnTypeCriterion.java new file mode 100644 index 0000000..e559927 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/ReturnTypeCriterion.java
@@ -0,0 +1,70 @@ +package annotator.find; + +import annotator.Main; +import annotator.scanner.CommonScanner; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class ReturnTypeCriterion implements Criterion { + + private final String methodName; + private final Criterion inClassCriterion; + private final Criterion sigMethodCriterion; + + public ReturnTypeCriterion(String className, String methodName) { + this.methodName = methodName; + this.inClassCriterion = Criteria.inClass(className, false); + this.sigMethodCriterion = methodName.isEmpty() ? null + : Criteria.isSigMethod(methodName); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null) { return false; } + + Criteria.dbug.debug("ReturnTypeCriterion.isSatisfiedBy(%s); this=%n", + Main.pathToString(path), this.toString()); + + do { + if (path.getLeaf().getKind() == Tree.Kind.METHOD) { + if (sigMethodCriterion == null + || sigMethodCriterion.isSatisfiedBy(path)) { + // Method and return type verified; now check class. + path = path.getParentPath(); + while (path != null && path.getLeaf() != null) { + if (CommonScanner.hasClassKind(path.getLeaf())) { + if (!inClassCriterion.isSatisfiedBy(path)) { break; } + Criteria.dbug.debug("ReturnTypeCriterion.isSatisfiedBy => true%n"); + return true; + } + path = path.getParentPath(); + } + } + break; + } + path = path.getParentPath(); + } while (path != null && path.getLeaf() != null); + + Criteria.dbug.debug("ReturnTypeCriterion.isSatisfiedBy => false%n"); + return false; + } + + @Override + public Kind getKind() { + return Kind.RETURN_TYPE; + } + + @Override + public String toString() { + return "ReturnTypeCriterion for method: " + methodName; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/TreeFinder.java b/annotation-file-utilities/src/annotator/find/TreeFinder.java new file mode 100644 index 0000000..47443e8 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/TreeFinder.java
@@ -0,0 +1,1847 @@ +package annotator.find; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.Name; +import javax.lang.model.type.NullType; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimaps; +import com.google.common.collect.SetMultimap; +import com.sun.source.tree.AnnotatedTypeTree; +import com.sun.source.tree.AnnotationTree; +import com.sun.source.tree.ArrayTypeTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.CompilationUnitTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.IdentifierTree; +import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.MemberSelectTree; +import com.sun.source.tree.MethodTree; +import com.sun.source.tree.ModifiersTree; +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.ParameterizedTypeTree; +import com.sun.source.tree.PrimitiveTypeTree; +import com.sun.source.tree.Tree; +import com.sun.source.tree.TypeCastTree; +import com.sun.source.tree.TypeParameterTree; +import com.sun.source.tree.VariableTree; +import com.sun.source.tree.WildcardTree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreeScanner; + +import com.sun.tools.javac.code.Flags; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCAnnotatedType; +import com.sun.tools.javac.tree.JCTree.JCAnnotation; +import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; +import com.sun.tools.javac.tree.JCTree.JCExpression; +import com.sun.tools.javac.tree.JCTree.JCFieldAccess; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCModifiers; +import com.sun.tools.javac.tree.JCTree.JCNewArray; +import com.sun.tools.javac.tree.JCTree.JCNewClass; +import com.sun.tools.javac.tree.JCTree.JCTypeApply; +import com.sun.tools.javac.tree.JCTree.JCTypeParameter; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.JCWildcard; +import com.sun.tools.javac.util.Position; + +import annotations.io.ASTIndex; +import annotations.io.ASTPath; +import annotations.io.ASTRecord; +import annotations.io.DebugWriter; +import annotator.Main; +import annotator.scanner.CommonScanner; +import annotator.specification.IndexFileSpecification; + +import plume.Pair; + +import type.DeclaredType; +import type.Type; + +/** + * A {@link TreeScanner} that is able to locate program elements in an + * AST based on {@code Criteria}. {@link #getInsertionsByPosition(JCTree.JCCompilationUnit,List)} + * scans a tree and returns a + * mapping of source positions (as character offsets) to insertion text. + */ +public class TreeFinder extends TreeScanner<Void, List<Insertion>> { + public static final DebugWriter dbug = new DebugWriter(); + public static final DebugWriter stak = new DebugWriter(); + public static final DebugWriter warn = new DebugWriter(); + + /** + * String representation of regular expression matching a comment in + * Java code. The part before {@code |} matches a single-line + * comment, and the part after matches a multi-line comment, which + * breaks down as follows (adapted from + * <a href="http://perldoc.perl.org/perlfaq6.html#How-do-I-use-a-regular-expression-to-strip-C-style-comments-from-a-file%3f">Perl FAQ</a>): + * <pre> + * /\* ## Start of comment + * [^*]*\*+ ## Non-* followed by 1-or-more *s + * ( + * [^/*][^*]*\*+ + * )* ## 0 or more things which don't start with / + * ## but do end with '*' + * / ## End of comment + * </pre> + * Note: Care must be taken to avoid false comment matches starting + * inside a string literal. Ensuring that the code segment being + * matched starts at an AST node boundary is sufficient to prevent + * this complication. + */ + private final static String comment = + "//.*$|/\\*[^*]*+\\*++(?:[^*/][^*]*+\\*++)*+/"; + + /** + * Regular expression matching a character or string literal. + */ + private final static String literal = + "'(?:(?:\\\\(?:'|[^']*+))|[^\\\\'])'|\"(?:\\\\.|[^\\\\\"])*\""; + + /** + * Regular expression matching a non-commented instance of {@code /} + * that is not part of a comment-starting delimiter. + */ + private final static String nonDelimSlash = "/(?=[^*/])"; + + /** + * Returns regular expression matching "anything but" {@code c}: a + * single comment, character or string literal, or non-{@code c} + * character. + */ + private final static String otherThan(char c) { + String cEscaped; + + // escape if necessary for use in character class + switch (c) { + case '/': + case '"': + case '\'': + cEscaped = ""; break; // already present in class defn + case '\\': + case '[': + case ']': + cEscaped = "\\" + c; break; // escape! + default: + cEscaped = "" + c; + } + + return "[^/'" + cEscaped + "\"]|" + "|" + literal + "|" + comment + + (c == '/' ? "" : nonDelimSlash); + } + + // If this code location is not an array type, return null. Otherwise, + // starting at an array type, walk up the AST as long as still an array, + // and stop at the largest containing array (with nothing but arrays in + // between). + public static TreePath largestContainingArray(TreePath p) { + if (p.getLeaf().getKind() != Tree.Kind.ARRAY_TYPE) { + return null; + } + while (p.getParentPath().getLeaf().getKind() == Tree.Kind.ARRAY_TYPE) { + p = p.getParentPath(); + } + assert p.getLeaf().getKind() == Tree.Kind.ARRAY_TYPE; + return p; + } + + /** + * Returns the position of the first (non-commented) instance of a + * character at or after the given position. (Assumes position is not + * inside a comment.) + * + * @see #getNthInstanceBetween(char, int, int, int, CompilationUnitTree) + */ + private int getFirstInstanceAfter(char c, int i) { + return getNthInstanceInRange(c, i, Integer.MAX_VALUE, 1); + } + + /** + * Returns the position of the {@code n}th (non-commented, non-quoted) + * instance of a character between the given positions, or the last + * instance if {@code n==0}. (Assumes position is not inside a + * comment.) + * + * @param c the character being sought + * @param start position at which the search starts (inclusive) + * @param end position at which the search ends (exclusive) + * @param n number of repetitions, or 0 for last occurrence + * @return position of match in {@code tree}, or + * {@link Position.NOPOS} if match not found + */ + private int getNthInstanceInRange(char c, int start, int end, int n) { + if (end < 0) { + throw new IllegalArgumentException("negative end position"); + } + if (n < 0) { + throw new IllegalArgumentException("negative count"); + } + + try { + CharSequence s = tree.getSourceFile().getCharContent(true); + int count = n; + int pos = Position.NOPOS; + int stop = Math.min(end, s.length()); + String cQuoted = c == '/' ? nonDelimSlash : Pattern.quote("" + c); + String regex = "(?:" + otherThan(c) + ")*+" + cQuoted; + Pattern p = Pattern.compile(regex, Pattern.MULTILINE); + Matcher m = p.matcher(s).region(start, stop); + + // using n==0 for "last" ensures that {@code (--n == 0)} is always + // false, (reasonably) assuming no underflow + while (m.find()) { + pos = m.end() - 1; + if (--count == 0) { break; } + } + // positive count means search halted before nth instance was found + return count > 0 ? Position.NOPOS : pos; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + // Find a node's parent in the current source tree. + private Tree parent(Tree node) { + return getPath(node).getParentPath().getLeaf(); + } + + /** + * An alternative to TreePath.getPath(CompilationUnitTree,Tree) that + * caches its results. + */ + public TreePath getPath(Tree target) { + if (treePathCache.containsKey(target)) { + return treePathCache.get(target); + } + TreePath result = TreePath.getPath(tree, target); + treePathCache.put(target, result); + return result; + } + + Map<Tree, TreePath> treePathCache = new HashMap<Tree, TreePath>(); + + private ASTRecord astRecord(Tree node) { + Map<Tree, ASTRecord> index = ASTIndex.indexOf(tree); + return index.get(node); + } + + /** + * Determines the insertion position for type annotations on various + * elements. For instance, type annotations for a declaration should be + * placed before the type rather than the variable name. + */ + private class TypePositionFinder + extends TreeScanner<Pair<ASTRecord, Integer>, Insertion> { + private Pair<ASTRecord, Integer> pathAndPos(JCTree t) { + return Pair.of(astRecord(t), t.pos); + } + + private Pair<ASTRecord, Integer> pathAndPos(JCTree t, int i) { + return Pair.of(astRecord(t), i); + } + + /** @param t an expression for a type */ + private Pair<ASTRecord, Integer> getBaseTypePosition(JCTree t) { + while (true) { + switch (t.getKind()) { + case IDENTIFIER: + case PRIMITIVE_TYPE: + return pathAndPos(t); + case MEMBER_SELECT: + JCTree exp = t; + do { // locate pkg name, if any + JCFieldAccess jfa = (JCFieldAccess) exp; + exp = jfa.getExpression(); + if (jfa.sym.isStatic()) { + return pathAndPos(exp, + getFirstInstanceAfter('.', + exp.getEndPosition(tree.endPositions)) + 1); + } + } while (exp instanceof JCFieldAccess + && ((JCFieldAccess) exp).sym.getKind() != ElementKind.PACKAGE); + if (exp != null) { + if (exp.getKind() == Tree.Kind.IDENTIFIER) { + Symbol sym = ((JCIdent) exp).sym; + if (!(sym.isStatic() || sym.getKind() == ElementKind.PACKAGE)) { + return pathAndPos(t, t.getStartPosition()); + } + } + t = exp; + } + return pathAndPos(t, + getFirstInstanceAfter('.', + t.getEndPosition(tree.endPositions)) + 1); + case ARRAY_TYPE: + t = ((JCArrayTypeTree) t).elemtype; + break; + case PARAMETERIZED_TYPE: + return pathAndPos(t, t.getStartPosition()); + case EXTENDS_WILDCARD: + case SUPER_WILDCARD: + t = ((JCWildcard) t).inner; + break; + case UNBOUNDED_WILDCARD: + // This is "?" as in "List<?>". ((JCWildcard) t).inner is null. + // There is nowhere to attach the annotation, so for now return + // the "?" tree itself. + return pathAndPos(t); + case ANNOTATED_TYPE: + // If this type already has annotations on it, get the underlying + // type, without annotations. + t = ((JCAnnotatedType) t).underlyingType; + break; + default: + throw new RuntimeException(String.format("Unrecognized type (kind=%s, class=%s): %s", t.getKind(), t.getClass(), t)); + } + } + } + + @Override + public Pair<ASTRecord, Integer> visitVariable(VariableTree node, Insertion ins) { + Name name = node.getName(); + JCVariableDecl jn = (JCVariableDecl) node; + JCTree jt = jn.getType(); + Criteria criteria = ins.getCriteria(); + dbug.debug("visitVariable: %s %s%n", jt, jt.getClass()); + if (name != null && criteria.isOnFieldDeclaration()) { + return Pair.of(astRecord(node), jn.getStartPosition()); + } + if (jt instanceof JCTypeApply) { + JCExpression type = ((JCTypeApply) jt).clazz; + return pathAndPos(type); + } + return Pair.of(astRecord(node), jn.pos); + } + + // When a method is visited, it is visited for the receiver, not the + // return value and not the declaration itself. + @Override + public Pair<ASTRecord, Integer> visitMethod(MethodTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitMethod%n"); + super.visitMethod(node, ins); + + JCMethodDecl jcnode = (JCMethodDecl) node; + JCVariableDecl jcvar = (JCVariableDecl) node.getReceiverParameter(); + if (jcvar != null) { return pathAndPos(jcvar); } + + int pos = Position.NOPOS; + ASTRecord astPath = astRecord(jcnode) + .extend(Tree.Kind.METHOD, ASTPath.PARAMETER, -1); + + if (node.getParameters().isEmpty()) { + // no parameters; find first (uncommented) '(' after method name + pos = findMethodName(jcnode); + if (pos >= 0) { pos = getFirstInstanceAfter('(', pos); } + if (++pos <= 0) { + throw new RuntimeException("Couldn't find param opening paren for: " + + jcnode); + } + } else { + pos = ((JCTree) node.getParameters().get(0)).getStartPosition(); + } + return Pair.of(astPath, pos); + } + + @Override + public Pair<ASTRecord, Integer> visitIdentifier(IdentifierTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitIdentifier(%s)%n", node); + // for arrays, need to indent inside array, not right before type + ASTRecord rec = ASTIndex.indexOf(tree).get(node); + ASTPath astPath = ins.getCriteria().getASTPath(); + Tree parent = parent(node); + Integer i = null; + JCIdent jcnode = (JCIdent) node; + + // ASTPathEntry.type _n_ is a special case because it does not + // correspond to a node in the AST. + if (parent.getKind() == Tree.Kind.NEW_ARRAY) { // NewArrayTree) + ASTPath.ASTEntry entry; + dbug.debug("TypePositionFinder.visitIdentifier: recognized array%n"); + if (astPath == null) { + entry = new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0); + astPath = astRecord(parent).extend(entry).astPath; + } else { + entry = astPath.get(astPath.size() - 1); // kind is NewArray + } + if (entry.childSelectorIs(ASTPath.TYPE)) { + int n = entry.getArgument(); + i = jcnode.getStartPosition(); + if (n < getDimsSize((JCExpression) parent)) { // else n == #dims + i = getNthInstanceInRange('[', i, + ((JCNewArray) parent).getEndPosition(tree.endPositions), n+1); + } + } + if (i == null) { + i = jcnode.getEndPosition(tree.endPositions); + } + } else if (parent.getKind() == Tree.Kind.NEW_CLASS) { // NewClassTree) + dbug.debug("TypePositionFinder.visitIdentifier: recognized class%n"); + JCNewClass nc = (JCNewClass) parent; + dbug.debug( + "TypePositionFinder.visitIdentifier: clazz %s (%d) constructor %s%n", + nc.clazz, nc.clazz.getPreferredPosition(), nc.constructor); + i = nc.clazz.getPreferredPosition(); + if (astPath == null) { + astPath = astRecord(node).astPath; + } + } else { + ASTRecord astRecord = astRecord(node); + astPath = astRecord.astPath; + i = ((JCIdent) node).pos; + } + + dbug.debug("visitIdentifier(%s) => %d where parent (%s) = %s%n", + node, i, parent.getClass(), parent); + return Pair.of(rec.replacePath(astPath), i); + } + + @Override + public Pair<ASTRecord, Integer> visitMemberSelect(MemberSelectTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitMemberSelect(%s)%n", node); + JCFieldAccess raw = (JCFieldAccess) node; + return Pair.of(astRecord(node), + raw.getEndPosition(tree.endPositions) - raw.name.length()); + } + + @Override + public Pair<ASTRecord, Integer> visitTypeParameter(TypeParameterTree node, Insertion ins) { + JCTypeParameter tp = (JCTypeParameter) node; + return Pair.of(astRecord(node), tp.getStartPosition()); + } + + @Override + public Pair<ASTRecord, Integer> visitWildcard(WildcardTree node, Insertion ins) { + JCWildcard wc = (JCWildcard) node; + return Pair.of(astRecord(node), wc.getStartPosition()); + } + + @Override + public Pair<ASTRecord, Integer> visitPrimitiveType(PrimitiveTypeTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitPrimitiveType(%s)%n", node); + return pathAndPos((JCTree) node); + } + + @Override + public Pair<ASTRecord, Integer> visitParameterizedType(ParameterizedTypeTree node, Insertion ins) { + Tree parent = parent(node); + dbug.debug("TypePositionFinder.visitParameterizedType %s parent=%s%n", + node, parent); + Integer pos = getBaseTypePosition(((JCTypeApply) node).getType()).b; + return Pair.of(astRecord(node), pos); + } + + /** + * Returns the number of array levels that are in the given array type tree, + * or 0 if the given node is not an array type tree. + */ + private int arrayLevels(com.sun.tools.javac.code.Type t) { + return t.accept(new Types.SimpleVisitor<Integer, Integer>() { + @Override + public Integer visitArrayType(com.sun.tools.javac.code.Type.ArrayType t, + Integer i) { + return t.elemtype.accept(this, i+1); + } + @Override + public Integer visitType(com.sun.tools.javac.code.Type t, Integer i) { + return i; + } + }, 0); + } + + private int arrayLevels(Tree node) { + int result = 0; + while (node.getKind() == Tree.Kind.ARRAY_TYPE) { + result++; + node = ((ArrayTypeTree) node).getType(); + } + return result; + } + + private JCTree arrayContentType(JCArrayTypeTree att) { + JCTree node = att; + do { + node = ((JCArrayTypeTree) node).getType(); + } while (node.getKind() == Tree.Kind.ARRAY_TYPE); + return node; + } + + private ArrayTypeTree largestContainingArray(Tree node) { + TreePath p = getPath(node); + Tree result = TreeFinder.largestContainingArray(p).getLeaf(); + assert result.getKind() == Tree.Kind.ARRAY_TYPE; + return (ArrayTypeTree) result; + } + + @Override + public Pair<ASTRecord, Integer> visitArrayType(ArrayTypeTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitArrayType(%s)%n", node); + JCArrayTypeTree att = (JCArrayTypeTree) node; + dbug.debug("TypePositionFinder.visitArrayType(%s) preferred = %s%n", + node, att.getPreferredPosition()); + // If the code has a type like "String[][][]", then this gets called + // three times: for String[][][], String[][], and String[] + // respectively. For each of the three, call String[][][] "largest". + ArrayTypeTree largest = largestContainingArray(node); + int largestLevels = arrayLevels(largest); + int levels = arrayLevels(node); + int start = arrayContentType(att).getPreferredPosition() + 1; + int end = att.getEndPosition(tree.endPositions); + int pos = arrayInsertPos(start, end); + + dbug.debug(" levels=%d largestLevels=%d%n", levels, largestLevels); + for (int i=levels; i<largestLevels; i++) { + pos = getFirstInstanceAfter('[', pos+1); + dbug.debug(" pos %d at i=%d%n", pos, i); + } + return Pair.of(astRecord(node), pos); + } + + /** + * Find position in source code where annotation is to be inserted. + * + * @param start beginning of range to be matched + * @param end end of range to be matched + * + * @return position for annotation insertion + */ + private int arrayInsertPos(int start, int end) { + try { + CharSequence s = tree.getSourceFile().getCharContent(true); + int pos = getNthInstanceInRange('[', start, end, 1); + + if (pos < 0) { + // no "[", so check for "..." + String nonDot = otherThan('.'); + String regex = "(?:(?:\\.\\.?)?" + nonDot + ")*(\\.\\.\\.)"; + Pattern p = Pattern.compile(regex, Pattern.MULTILINE); + Matcher m = p.matcher(s).region(start, end); + + if (m.find()) { + pos = m.start(1); + } + if (pos < 0) { // should never happen + throw new RuntimeException("no \"[\" or \"...\" in array type"); + } + } + return pos; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public Pair<ASTRecord, Integer> visitCompilationUnit(CompilationUnitTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitCompilationUnit%n"); + JCCompilationUnit cu = (JCCompilationUnit) node; + return Pair.of(astRecord(node), cu.getStartPosition()); + } + + @Override + public Pair<ASTRecord, Integer> visitClass(ClassTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitClass%n"); + JCClassDecl cd = (JCClassDecl) node; + JCTree t = cd.mods == null ? cd : cd.mods; + return Pair.of(astRecord(cd), t.getPreferredPosition()); + } + + // There are three types of array initializers: + // /*style 1*/ String[] names1 = new String[12]; + // /*style 2*/ String[] names2 = { "Alice", "Bob" }; + // /*style 3*/ String[] names3 = new String[] { "Alice", "Bob" }; + // (Can the styles be combined?) + // + // For style 1, we can just find the location of the + // dimensionality expression, and then locate the bracket before it. + // For style 2, annotations are impossible. + // For style 3, we need to count the brackets and get to the right one. + // + // The AST depth of the initializer is correct unless all arrays are + // empty, in which case it is arbitary. This is legal: + // String[][][][][] names4 = new String[][][][][] { { { } } }; + // + // Array initializers can also be multi-dimensional, but this is not + // relevant to us: + // int[][] pascalsTriangle = { { 1 }, { 1,1 }, { 1,2,1 } }; + // int[][] pascalsTriangle = new int[][] { { 1 }, { 1,1 }, { 1,2,1 } }; + + // structure stolen from javac's Pretty.java + private int getDimsSize(JCExpression tree) { + if (tree instanceof JCNewArray) { + JCNewArray na = (JCNewArray) tree; + if (na.dims.size() != 0) { + // when not all dims are given, na.dims.size() gives wrong answer + return arrayLevels(na.type); + } + if (na.elemtype != null) { + return getDimsSize(na.elemtype) + 1; + } + assert na.elems != null; + int maxDimsSize = 0; + for (JCExpression elem : na.elems) { + if (elem instanceof JCNewArray) { + int elemDimsSize = getDimsSize((JCNewArray)elem); + maxDimsSize = Math.max(maxDimsSize, elemDimsSize); + } else if (elem instanceof JCArrayTypeTree) { + // Does this ever happen? javac's Pretty.java handles it. + System.out.printf("JCArrayTypeTree: %s%n", elem); + } + } + return maxDimsSize + 1; + } else if (tree instanceof JCAnnotatedType) { + return getDimsSize(((JCAnnotatedType) tree).underlyingType); + } else if (tree instanceof JCArrayTypeTree) { + return 1 + getDimsSize(((JCArrayTypeTree) tree).elemtype); + } else { + return 0; + } + } + + + // Visit an expression of one of these forms: + // new int[5][10] + // new int[][] {...} + // { ... } -- as in: String[] names2 = { "Alice", "Bob" }; + @Override + public Pair<ASTRecord, Integer> visitNewArray(NewArrayTree node, Insertion ins) { + dbug.debug("TypePositionFinder.visitNewArray%n"); + JCNewArray na = (JCNewArray) node; + GenericArrayLocationCriterion galc = + ins.getCriteria().getGenericArrayLocation(); + ASTRecord rec = ASTIndex.indexOf(tree).get(node); + ASTPath astPath = ins.getCriteria().getASTPath(); + String childSelector = null; + // Invariant: na.dims.size() == 0 or na.elems == null (but not both) + // If na.dims.size() != 0, na.elemtype is non-null. + // If na.dims.size() == 0, na.elemtype may be null or non-null. + int dimsSize = getDimsSize(na); + int dim = galc == null ? 0 : galc.getLocation().size(); + + if (astPath == null) { + astPath = astRecord(node).astPath.extendNewArray(dim); + childSelector = ASTPath.TYPE; + } else { + ASTPath.ASTEntry lastEntry = null; + int n = astPath.size(); + int i = n; + // find matching node = last path entry w/kind NEW_ARRAY + while (--i >= 0) { + lastEntry = astPath.get(i); + if (lastEntry.getTreeKind() == Tree.Kind.NEW_ARRAY) { break; } + } + assert i >= 0 : "no matching path entry (kind=NEW_ARRAY)"; + if (n > i+1) { + // find correct node further down and visit if present + assert dim + 1 == dimsSize; + Tree typeTree = na.elemtype; + int j = i + dim + 1; + while (--dim >= 0) { + typeTree = ((ArrayTypeTree) typeTree).getType(); + } +loop: + while (j < n) { + ASTPath.ASTEntry entry = astPath.get(j); + switch (entry.getTreeKind()) { + case ANNOTATED_TYPE: + typeTree = ((AnnotatedTypeTree) typeTree).getUnderlyingType(); + continue; // no increment + case ARRAY_TYPE: + typeTree = ((ArrayTypeTree) typeTree).getType(); + break; + case MEMBER_SELECT: + if (typeTree instanceof JCTree.JCFieldAccess) { + JCTree.JCFieldAccess jfa = (JCTree.JCFieldAccess) typeTree; + typeTree = jfa.getExpression(); + // if just a qualifier, don't increment loop counter + if (jfa.sym.getKind() == ElementKind.PACKAGE) { continue; } + break; + } + break loop; + case PARAMETERIZED_TYPE: + if (entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) { + int arg = entry.getArgument(); + List<? extends Tree> typeArgs = + ((ParameterizedTypeTree) typeTree).getTypeArguments(); + typeTree = typeArgs.get(arg); + } else { // ASTPath.TYPE + typeTree = ((ParameterizedTypeTree) typeTree).getType(); + } + break; + default: + break loop; + } + ++j; + } + if (j < n) { + // sought node is absent, so return default; insertion can + // be applied only as an inner of some TypedInsertion anyway + return getBaseTypePosition(na); + } + return typeTree.accept(this, ins); + } + + childSelector = lastEntry.getChildSelector(); + if (dim > 0 && ASTPath.TYPE.equals(childSelector)) { + // rebuild path with current value of dim + ASTPath newPath = ASTPath.empty(); + int j = 0; + dim += lastEntry.getArgument(); + while (j < i) { // [0,i) + newPath = newPath.extend(astPath.get(j)); + j++; + } + lastEntry = new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, + ASTPath.TYPE, dim); // i + newPath = newPath.extend(lastEntry); + while (j < n) { // [i,n) + newPath = newPath.extend(astPath.get(j)); + j++; + } + astPath = newPath; + } else { + dim = lastEntry.getArgument(); + } + } + + if (ASTPath.TYPE.equals(childSelector)) { + if (na.toString().startsWith("{")) { + if (ins.getKind() == Insertion.Kind.ANNOTATION) { + TreePath parentPath = TreePath.getPath(tree, na).getParentPath(); + if (parentPath != null) { + Tree parent = parentPath.getLeaf(); + if (parent.getKind() == Tree.Kind.VARIABLE) { + AnnotationInsertion ai = (AnnotationInsertion) ins; + JCTree typeTree = ((JCVariableDecl) parent).getType(); + ai.setType(typeTree.toString()); + return Pair.of(rec.replacePath(astPath), na.getStartPosition()); + } + } + System.err.println("WARNING: array initializer " + node + + " has no explicit type; skipping insertion " + ins); + return null; + } else { + return Pair.of(rec.replacePath(astPath), na.getStartPosition()); + } + } + if (dim == dimsSize) { + if (na.elemtype == null) { + System.err.println("WARNING: array initializer " + node + + " has no explicit type; skipping insertion " + ins); + return null; + } + return getBaseTypePosition(na.elemtype); + } + if (na.dims.size() != 0) { + int startPos = na.getStartPosition(); + int endPos = na.getEndPosition(tree.endPositions); + int pos = getNthInstanceInRange('[', startPos, endPos, dim + 1); + return Pair.of(rec.replacePath(astPath), pos); + } + // In a situation like + // node=new String[][][][][]{{{}}} + // Also see Pretty.printBrackets. + if (dim == 0) { + if (na.elemtype == null) { + return Pair.of(rec.replacePath(astPath), na.getStartPosition()); + } + // na.elemtype.getPreferredPosition(); seems to be at the end, + // after the brackets. + // na.elemtype.getStartPosition(); is before the type name itself. + int startPos = na.elemtype.getStartPosition(); + return Pair.of(rec.replacePath(astPath), + getFirstInstanceAfter('[', startPos+1)); + } else if (dim == dimsSize) { + return Pair.of(rec.replacePath(astPath), + na.getType().pos().getStartPosition()); + } else { + JCArrayTypeTree jcatt = (JCArrayTypeTree) na.elemtype; + for (int i=1; i<dim; i++) { + JCTree elem = jcatt.elemtype; + if (elem.hasTag(JCTree.Tag.ANNOTATED_TYPE)) { + elem = ((JCAnnotatedType) elem).underlyingType; + } + assert elem.hasTag(JCTree.Tag.TYPEARRAY); + jcatt = (JCArrayTypeTree) elem; + } + return Pair.of(rec.replacePath(astPath), + jcatt.pos().getPreferredPosition()); + } + } else if (ASTPath.DIMENSION.equals(childSelector)) { + List<JCExpression> inits = na.getInitializers(); + if (dim < inits.size()) { + JCExpression expr = inits.get(dim); + return Pair.of(astRecord(expr), expr.getStartPosition()); + } + return null; + } else if (ASTPath.INITIALIZER.equals(childSelector)) { + JCExpression expr = na.getDimensions().get(dim); + return Pair.of(astRecord(expr), expr.getStartPosition()); + } else { + assert false : "Unexpected child selector in AST path: " + + (childSelector == null ? "null" : "\"" + childSelector + "\""); + return null; + } + } + + @Override + public Pair<ASTRecord, Integer> visitNewClass(NewClassTree node, Insertion ins) { + JCNewClass na = (JCNewClass) node; + JCExpression className = na.clazz; + // System.out.printf("classname %s (%s)%n", className, className.getClass()); + while (! (className.getKind() == Tree.Kind.IDENTIFIER)) { // IdentifierTree + if (className instanceof JCAnnotatedType) { + className = ((JCAnnotatedType) className).underlyingType; + } else if (className instanceof JCTypeApply) { + className = ((JCTypeApply) className).clazz; + } else if (className instanceof JCFieldAccess) { + // This occurs for fully qualified names, e.g. "new java.lang.Object()". + // I'm not quite sure why the field "selected" is taken, but "name" would + // be a type mismatch. It seems to work, see NewPackage test case. + className = ((JCFieldAccess) className).selected; + } else { + throw new Error(String.format("unrecognized JCNewClass.clazz (%s): %s%n" + + " surrounding new class tree: %s%n", className.getClass(), className, node)); + } + // System.out.printf("classname %s (%s)%n", className, className.getClass()); + } + + return visitIdentifier((IdentifierTree) className, ins); + } + } + + /** + * Determine the insertion position for declaration annotations on + * various elements. For instance, method declaration annotations should + * be placed before all the other modifiers and annotations. + */ + private class DeclarationPositionFinder extends TreeScanner<Integer, Void> { + + // When a method is visited, it is visited for the declaration itself. + @Override + public Integer visitMethod(MethodTree node, Void p) { + super.visitMethod(node, p); + + // System.out.printf("DeclarationPositionFinder.visitMethod()%n"); + + ModifiersTree mt = node.getModifiers(); + + // actually List<JCAnnotation>. + List<? extends AnnotationTree> annos = mt.getAnnotations(); + // Set<Modifier> flags = mt.getFlags(); + + JCTree before; + if (annos.size() > 1) { + before = (JCAnnotation) annos.get(0); + } else if (node.getReturnType() != null) { + before = (JCTree) node.getReturnType(); + } else { + // if we're a constructor, we have null return type, so we use the constructor's position + // rather than the return type's position + before = (JCTree) node; + } + int declPos = before.getStartPosition(); + + // There is no source code location information for Modifiers, so + // cannot iterate through the modifiers. But we don't have to. + int modsPos = ((JCModifiers)mt).pos().getStartPosition(); + if (modsPos != Position.NOPOS) { + declPos = Math.min(declPos, modsPos); + } + + return declPos; + } + + @Override + public Integer visitCompilationUnit(CompilationUnitTree node, Void p) { + JCCompilationUnit cu = (JCCompilationUnit) node; + return cu.getStartPosition(); + } + + @Override + public Integer visitClass(ClassTree node, Void p) { + JCClassDecl cd = (JCClassDecl) node; + int result = -1; + if (cd.mods != null + && (cd.mods.flags != 0 || cd.mods.annotations.size() > 0)) { + result = cd.mods.getPreferredPosition(); + } + if (result < 0) { + result = cd.getPreferredPosition(); + } + assert result >= 0 || cd.name.isEmpty() + : String.format("%d %d %d%n", cd.getStartPosition(), + cd.getPreferredPosition(), cd.pos); + return result < 0 ? null : result; + } + + } + + private final TypePositionFinder tpf; + private final DeclarationPositionFinder dpf; + private final JCCompilationUnit tree; + private final SetMultimap<Pair<Integer, ASTPath>, Insertion> insertions; + private final SetMultimap<ASTRecord, Insertion> astInsertions; + + /** + * Creates a {@code TreeFinder} from a source tree. + * + * @param tree the source tree to search + */ + public TreeFinder(JCCompilationUnit tree) { + this.tree = tree; + this.insertions = LinkedHashMultimap.create(); + this.astInsertions = LinkedHashMultimap.create(); + this.tpf = new TypePositionFinder(); + this.dpf = new DeclarationPositionFinder(); + } + + // which nodes are possible insertion sites + boolean handled(Tree node) { + switch (node.getKind()) { + case ANNOTATION: + case ARRAY_TYPE: + case CLASS: + case COMPILATION_UNIT: + case ENUM: + case EXPRESSION_STATEMENT: + case EXTENDS_WILDCARD: + case IDENTIFIER: + case INTERFACE: + case METHOD: + case NEW_ARRAY: + case NEW_CLASS: + case PARAMETERIZED_TYPE: + case PRIMITIVE_TYPE: + case SUPER_WILDCARD: + case TYPE_PARAMETER: + case UNBOUNDED_WILDCARD: + case VARIABLE: + return true; + default: + return node instanceof ExpressionTree; + } + } + + /** + * Determines if the last {@link TypePathEntry} in the given list is a + * {@link TypePathEntryKind#WILDCARD}. + * + * @param location the list to check + * @return {@code true} if the last {@link TypePathEntry} is a + * {@link TypePathEntryKind#WILDCARD}, {@code false} otherwise. + */ + private boolean wildcardLast(List<TypePathEntry> location) { + return location.get(location.size() - 1).tag == TypePathEntryKind.WILDCARD; + } + + /** + * Scans this tree, using the list of insertions to generate the source + * position to insertion text mapping. Insertions are removed from the + * list when positions are found for them. + * + * @param node AST node being considered for annotation insertions + * @param p list of insertions not yet placed + * <p> + * When a match is found, this routine removes the insertion from p and + * adds it to the insertions map as a value, with a key that is a pair. + * On return, p contains only the insertions for which no match was found. + */ + @Override + public Void scan(Tree node, List<Insertion> p) { + if (node == null) { + return null; + } + + dbug.debug("SCANNING: %s %s%n", node.getKind(), node); + if (! handled(node)) { + dbug.debug("Not handled, skipping (%s): %s%n", node.getClass(), node); + // nothing to do + return super.scan(node, p); + } + + TreePath path = getPath(node); + assert path == null || path.getLeaf() == node : + String.format("Mismatch: '%s' '%s' '%s'%n", + path, path.getLeaf(), node); + + // To avoid annotating existing annotations right before + // the element you wish to annotate, skip anything inside of + // an annotation. + if (path != null) { + for (Tree t : path) { + if (t.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + // We started with something within a parameterized type and + // should not look for any further annotations. + // TODO: does this work on multiple nested levels? + break; + } + if (t.getKind() == Tree.Kind.ANNOTATION) { + return super.scan(node, p); + } + } + } + + for (Iterator<Insertion> it = p.iterator(); it.hasNext(); ) { + Insertion i = it.next(); + if (i.getInserted()) { + // Skip this insertion if it has already been inserted. See + // the ReceiverInsertion class for details. + it.remove(); + continue; + } + dbug.debug("Considering insertion at tree:%n"); + dbug.debug(" Insertion: %s%n", i); + dbug.debug(" First line of node: %s%n", Main.firstLine(node.toString())); + dbug.debug(" Type of node: %s%n", node.getClass()); + if (!i.getCriteria().isSatisfiedBy(path, node)) { + dbug.debug(" ... not satisfied%n"); + continue; + } else { + dbug.debug(" ... satisfied!%n"); + dbug.debug(" First line of node: %s%n", Main.firstLine(node.toString())); + dbug.debug(" Type of node: %s%n", node.getClass()); + + ASTPath astPath = i.getCriteria().getASTPath(); + Integer pos = astPath == null ? findPosition(path, i) + : Main.convert_jaifs ? null // already in correct form + : findPositionByASTPath(astPath, path, i); + if (pos != null) { + dbug.debug(" ... satisfied! at %d for node of type %s: %s%n", + pos, node.getClass(), Main.treeToString(node)); + insertions.put(Pair.of(pos, astPath), i); + } + } + it.remove(); + } + return super.scan(node, p); + } + + // Find insertion position for Insertion whose criteria matched the + // given TreePath. + // If no position is found, report an error and return null. + Integer findPosition(TreePath path, Insertion i) { + Tree node = path.getLeaf(); + try { + // As per the JSR308 specification, receiver parameters are not allowed + // on method declarations of anonymous inner classes. + if (i.getCriteria().isOnReceiver() + && path.getParentPath().getParentPath().getLeaf().getKind() == Tree.Kind.NEW_CLASS) { + warn.debug("WARNING: Cannot insert a receiver parameter " + + "on a method declaration of an anonymous inner class. " + + "This insertion will be skipped.%n Insertion: %s%n", i); + return null; + } + + // TODO: Find a more fine-grained replacement for the 2nd conjunct below. + // The real issue is whether the insertion will add non-annotation code, + // which is only sometimes the case for a TypedInsertion. + if (alreadyPresent(path, i) && !(i instanceof TypedInsertion)) { + // Don't insert a duplicate if this particular annotation is already + // present at this location. + return null; + } + + if (i.getKind() == Insertion.Kind.CONSTRUCTOR) { + ConstructorInsertion cons = (ConstructorInsertion) i; + if (node.getKind() == Tree.Kind.METHOD) { + JCMethodDecl method = (JCMethodDecl) node; + // TODO: account for the following situation in matching phase instead + if (method.sym.owner.isAnonymous()) { return null; } + if ((method.mods.flags & Flags.GENERATEDCONSTR) != 0) { + addConstructor(path, cons, method); + } else { + cons.setAnnotationsOnly(true); + cons.setInserted(true); + i = cons.getReceiverInsertion(); + if (i == null) { return null; } + } + } else { + cons.setAnnotationsOnly(true); + } + } + + if (i.getKind() == Insertion.Kind.RECEIVER && node.getKind() == Tree.Kind.METHOD) { + ReceiverInsertion receiver = (ReceiverInsertion) i; + MethodTree method = (MethodTree) node; + VariableTree rcv = method.getReceiverParameter(); + + if (rcv == null) { + addReceiverType(path, receiver, method); + } + } + + if (i.getKind() == Insertion.Kind.NEW && node.getKind() == Tree.Kind.NEW_ARRAY) { + NewInsertion neu = (NewInsertion) i; + NewArrayTree newArray = (NewArrayTree) node; + + if (newArray.toString().startsWith("{")) { + addNewType(path, neu, newArray); + } + } + + // If this is a method, then it might have been selected because of + // the receiver, or because of the return value. Distinguish those. + // One way would be to set a global variable here. Another would be + // to look for a particular different node. I will do the latter. + Integer pos = Position.NOPOS; + + // The insertion location is at or below the matched location + // in the source tree. For example, a receiver annotation + // matches on the method and inserts on the (possibly newly + // created) receiver. + Map<Tree, ASTRecord> astIndex = ASTIndex.indexOf(tree); + ASTRecord insertRecord = astIndex.get(node); + dbug.debug("TreeFinder.scan: node=%s%n critera=%s%n", + node, i.getCriteria()); + + if (CommonScanner.hasClassKind(node) + && i.getCriteria().isOnTypeDeclarationExtendsClause() + && ((ClassTree) node).getExtendsClause() == null) { + return implicitClassBoundPosition((JCClassDecl) node, i); + } + if (node.getKind() == Tree.Kind.METHOD + && i.getCriteria().isOnReturnType()) { + JCMethodDecl jcnode = (JCMethodDecl) node; + Tree returnType = jcnode.getReturnType(); + insertRecord = insertRecord.extend(Tree.Kind.METHOD, ASTPath.TYPE); + if (returnType == null) { + // find constructor name instead + pos = findMethodName(jcnode); + if (pos < 0) { // skip -- inserted w/generated constructor + return null; + } + dbug.debug("pos = %d at constructor name: %s%n", + pos, jcnode.sym.toString()); + } else { + Pair<ASTRecord, Integer> pair = tpf.scan(returnType, i); + insertRecord = pair.a; + pos = pair.b; + assert handled(node); + dbug.debug("pos = %d at return type node: %s%n", + pos, returnType.getClass()); + } + } else if (node.getKind() == Tree.Kind.TYPE_PARAMETER + && i.getCriteria().onBoundZero() + && (((TypeParameterTree) node).getBounds().isEmpty() + || (((JCExpression) ((TypeParameterTree) node) + .getBounds().get(0))).type.tsym.isInterface()) + || (node instanceof WildcardTree + && ((WildcardTree) node).getBound() == null + && wildcardLast(i.getCriteria() + .getGenericArrayLocation().getLocation()))) { + Pair<ASTRecord, Integer> pair = tpf.scan(node, i); + insertRecord = pair.a; + pos = pair.b; + + if (i.getKind() == Insertion.Kind.ANNOTATION) { + if (node.getKind() == Tree.Kind.TYPE_PARAMETER + && !((TypeParameterTree) node).getBounds().isEmpty()) { + Tree bound = ((TypeParameterTree) node).getBounds().get(0); + pos = ((JCExpression) bound).getStartPosition(); + ((AnnotationInsertion) i).setGenerateBound(true); + } else { + int limit = ((JCTree) parent(node)).getEndPosition(tree.endPositions); + Integer nextpos1 = getNthInstanceInRange(',', pos+1, limit, 1); + Integer nextpos2 = getNthInstanceInRange('>', pos+1, limit, 1); + pos = (nextpos1 != Position.NOPOS && nextpos1 < nextpos2) ? nextpos1 : nextpos2; + ((AnnotationInsertion) i).setGenerateExtends(true); + } + } + } else if (i.getKind() == Insertion.Kind.CAST) { + Type t = ((CastInsertion) i).getType(); + JCTree jcTree = (JCTree) node; + pos = jcTree.getStartPosition(); + if (t.getKind() == Type.Kind.DECLARED) { + DeclaredType dt = (DeclaredType) t; + if (dt.getName().isEmpty()) { + dt.setName(jcTree.type instanceof NullType ? "Object" + : jcTree.type.toString()); + } + } + } else if (i.getKind() == Insertion.Kind.CLOSE_PARENTHESIS) { + JCTree jcTree = (JCTree) node; + pos = jcTree.getEndPosition(tree.endPositions); + } else { + boolean typeScan = true; + if (node.getKind() == Tree.Kind.METHOD) { // MethodTree + // looking for the receiver or the declaration + typeScan = i.getCriteria().isOnReceiver(); + } else if (CommonScanner.hasClassKind(node)) { // ClassTree + typeScan = ! i.getSeparateLine(); // hacky check + } + if (typeScan) { + // looking for the type + dbug.debug("Calling tpf.scan(%s: %s)%n", node.getClass(), node); + Pair<ASTRecord, Integer> pair = tpf.scan(node, i); + insertRecord = pair.a; + pos = pair.b; + assert handled(node); + dbug.debug("pos = %d at type: %s (%s)%n", pos, + node.toString(), node.getClass()); + } else if (node.getKind() == Tree.Kind.METHOD + && i.getKind() == Insertion.Kind.CONSTRUCTOR + && (((JCMethodDecl) node).mods.flags & Flags.GENERATEDCONSTR) != 0) { + Tree parent = path.getParentPath().getLeaf(); + pos = ((JCClassDecl) parent).getEndPosition(tree.endPositions) - 1; + insertRecord = null; // TODO + } else { + // looking for the declaration + pos = dpf.scan(node, null); + insertRecord = astRecord(node); + dbug.debug("pos = %s at declaration: %s%n", pos, node.getClass()); + } + } + + if (pos != null) { + assert pos >= 0 : + String.format("pos: %s%nnode: %s%ninsertion: %s%n", pos, node, i); + astInsertions.put(insertRecord, i); + } + return pos; + } catch (Throwable e) { + reportInsertionError(i, e); + return null; + } + } + + // Find insertion position for Insertion whose criteria (including one + // for the ASTPath) matched the given TreePath. + // If no position is found, report an error and return null. + Integer findPositionByASTPath(ASTPath astPath, TreePath path, Insertion i) { + Tree node = path.getLeaf(); + try { + ASTPath.ASTEntry entry = astPath.get(-1); + // As per the JSR308 specification, receiver parameters are not allowed + // on method declarations of anonymous inner classes. + if (entry.getTreeKind() == Tree.Kind.METHOD + && entry.childSelectorIs(ASTPath.PARAMETER) + && entry.getArgument() == -1 + && path.getParentPath().getParentPath().getLeaf().getKind() + == Tree.Kind.NEW_CLASS) { + warn.debug("WARNING: Cannot insert a receiver parameter " + + "on a method declaration of an anonymous inner class. " + + "This insertion will be skipped.%n Insertion: %s%n", i); + return null; + } + + if (alreadyPresent(path, i)) { + // Don't insert a duplicate if this particular annotation is already + // present at this location. + return null; + } + + if (i.getKind() == Insertion.Kind.CONSTRUCTOR) { + ConstructorInsertion cons = (ConstructorInsertion) i; + + if (node.getKind() == Tree.Kind.METHOD) { + JCMethodDecl method = (JCMethodDecl) node; + if ((method.mods.flags & Flags.GENERATEDCONSTR) != 0) { + addConstructor(path, cons, method); + } else { + cons.setAnnotationsOnly(true); + cons.setInserted(true); + i = cons.getReceiverInsertion(); + if (i == null) { return null; } + } + } else { + cons.setAnnotationsOnly(true); + } + } + + if (i.getKind() == Insertion.Kind.RECEIVER && node.getKind() == Tree.Kind.METHOD) { + ReceiverInsertion receiver = (ReceiverInsertion) i; + MethodTree method = (MethodTree) node; + + if (method.getReceiverParameter() == null) { + addReceiverType(path, receiver, method); + } + } + + if (i.getKind() == Insertion.Kind.NEW && node.getKind() == Tree.Kind.NEW_ARRAY) { + NewInsertion neu = (NewInsertion) i; + NewArrayTree newArray = (NewArrayTree) node; + + if (newArray.toString().startsWith("{")) { + addNewType(path, neu, newArray); + } + } + + // If this is a method, then it might have been selected because of + // the receiver, or because of the return value. Distinguish those. + // One way would be to set a global variable here. Another would be + // to look for a particular different node. I will do the latter. + Integer pos = Position.NOPOS; + + // The insertion location is at or below the matched location + // in the source tree. For example, a receiver annotation + // matches on the method and inserts on the (possibly newly + // created) receiver. + Map<Tree, ASTRecord> astIndex = ASTIndex.indexOf(tree); + ASTRecord insertRecord = astIndex.get(node); + dbug.debug("TreeFinder.scan: node=%s%n criteria=%s%n", + node, i.getCriteria()); + + if (CommonScanner.hasClassKind(node) + && entry.childSelectorIs(ASTPath.BOUND) + && entry.getArgument() < 0 + && ((ClassTree) node).getExtendsClause() == null) { + return implicitClassBoundPosition((JCClassDecl) node, i); + } + if (node.getKind() == Tree.Kind.METHOD + && i.getCriteria().isOnMethod("<init>()V") + && entry.childSelectorIs(ASTPath.PARAMETER) + && entry.getArgument() < 0) { + if (i.getKind() != Insertion.Kind.CONSTRUCTOR) { return null; } + Tree parent = path.getParentPath().getLeaf(); + insertRecord = insertRecord.extend(Tree.Kind.METHOD, ASTPath.PARAMETER, -1); + pos = ((JCTree) parent).getEndPosition(tree.endPositions) - 1; + } else if (node.getKind() == Tree.Kind.METHOD + && entry.childSelectorIs(ASTPath.TYPE)) { + JCMethodDecl jcnode = (JCMethodDecl) node; + Tree returnType = jcnode.getReturnType(); + insertRecord = insertRecord.extend(Tree.Kind.METHOD, ASTPath.TYPE); + if (returnType == null) { + // find constructor name instead + pos = findMethodName(jcnode); + if (pos < 0) { // skip -- inserted w/generated constructor + return null; + } + dbug.debug("pos = %d at constructor name: %s%n", + pos, jcnode.sym.toString()); + } else { + Pair<ASTRecord, Integer> pair = tpf.scan(returnType, i); + insertRecord = pair.a; + pos = pair.b; + assert handled(node); + dbug.debug("pos = %d at return type node: %s%n", + pos, returnType.getClass()); + } + } else if (node.getKind() == Tree.Kind.TYPE_PARAMETER + && entry.getTreeKind() == Tree.Kind.TYPE_PARAMETER // TypeParameter.bound + && (((TypeParameterTree) node).getBounds().isEmpty() + || (((JCExpression) ((TypeParameterTree) node) + .getBounds().get(0))).type.tsym.isInterface()) + || ASTPath.isWildcard(node.getKind()) + && (entry.getTreeKind() == Tree.Kind.TYPE_PARAMETER + || ASTPath.isWildcard(entry.getTreeKind())) + && entry.childSelectorIs(ASTPath.BOUND) + && (!entry.hasArgument() || entry.getArgument() == 0)) { + Pair<ASTRecord, Integer> pair = tpf.scan(node, i); + insertRecord = pair.a; + pos = pair.b; + + if (i.getKind() == Insertion.Kind.ANNOTATION) { + if (node.getKind() == Tree.Kind.TYPE_PARAMETER + && !((TypeParameterTree) node).getBounds().isEmpty()) { + Tree bound = ((TypeParameterTree) node).getBounds().get(0); + pos = ((JCExpression) bound).getStartPosition(); + ((AnnotationInsertion) i).setGenerateBound(true); + } else { + int limit = ((JCTree) parent(node)).getEndPosition(tree.endPositions); + Integer nextpos1 = getNthInstanceInRange(',', pos+1, limit, 1); + Integer nextpos2 = getNthInstanceInRange('>', pos+1, limit, 1); + pos = (nextpos1 != Position.NOPOS && nextpos1 < nextpos2) ? nextpos1 : nextpos2; + ((AnnotationInsertion) i).setGenerateExtends(true); + } + } + } else if (i.getKind() == Insertion.Kind.CAST) { + Type t = ((CastInsertion) i).getType(); + JCTree jcTree = (JCTree) node; + if (jcTree.getKind() == Tree.Kind.VARIABLE && !astPath.isEmpty() + && astPath.get(-1).childSelectorIs(ASTPath.INITIALIZER)) { + node = ((JCVariableDecl) node).getInitializer(); + if (node == null) { return null; } + jcTree = (JCTree) node; + } + pos = jcTree.getStartPosition(); + if (t.getKind() == Type.Kind.DECLARED) { + DeclaredType dt = (DeclaredType) t; + if (dt.getName().isEmpty()) { + if (jcTree.type instanceof NullType) { + dt.setName("Object"); + } else { + t = Insertions.TypeTree.conv(jcTree.type); + t.setAnnotations(dt.getAnnotations()); + ((CastInsertion) i).setType(t); + } + } + } + } else if (i.getKind() == Insertion.Kind.CLOSE_PARENTHESIS) { + JCTree jcTree = (JCTree) node; + if (jcTree.getKind() == Tree.Kind.VARIABLE && !astPath.isEmpty() + && astPath.get(-1).childSelectorIs(ASTPath.INITIALIZER)) { + node = ((JCVariableDecl) node).getInitializer(); + if (node == null) { return null; } + jcTree = (JCTree) node; + } + pos = jcTree.getEndPosition(tree.endPositions); + } else { + boolean typeScan = true; + if (node.getKind() == Tree.Kind.METHOD) { // MethodTree + // looking for the receiver or the declaration + typeScan = IndexFileSpecification.isOnReceiver(i.getCriteria()); + } else if (node.getKind() == Tree.Kind.CLASS) { // ClassTree + typeScan = ! i.getSeparateLine(); // hacky check + } + if (typeScan) { + // looking for the type + dbug.debug("Calling tpf.scan(%s: %s)%n", node.getClass(), node); + Pair<ASTRecord, Integer> pair = tpf.scan(node, i); + insertRecord = pair.a; + pos = pair.b; + assert handled(node); + dbug.debug("pos = %d at type: %s (%s)%n", + pos, node.toString(), node.getClass()); + } else if (node.getKind() == Tree.Kind.METHOD + && i.getKind() == Insertion.Kind.CONSTRUCTOR + && (((JCMethodDecl) node).mods.flags & Flags.GENERATEDCONSTR) != 0) { + Tree parent = path.getParentPath().getLeaf(); + pos = ((JCClassDecl) parent).getEndPosition(tree.endPositions) - 1; + insertRecord = null; // TODO + } else { + // looking for the declaration + pos = dpf.scan(node, null); + insertRecord = astRecord(node); + assert pos != null; + dbug.debug("pos = %d at declaration: %s%n", pos, node.getClass()); + } + } + + if (pos != null) { + assert pos >= 0 : + String.format("pos: %s%nnode: %s%ninsertion: %s%n", pos, node, i); + astInsertions.put(insertRecord, i); + } + return pos; + } catch (Throwable e) { + reportInsertionError(i, e); + return null; + } + } + + private Integer implicitClassBoundPosition(JCClassDecl cd, Insertion i) { + Integer pos; + if (cd.sym == null || cd.sym.isAnonymous() + || i.getKind() != Insertion.Kind.ANNOTATION) { + return null; + } + JCModifiers mods = cd.getModifiers(); + String name = cd.getSimpleName().toString(); + if (cd.typarams == null || cd.typarams.isEmpty()) { + int start = cd.getStartPosition(); + int offset = Math.max(start, + mods.getEndPosition(tree.endPositions) + 1); + String s = cd.toString().substring(offset - start); + Pattern p = Pattern.compile("(?:\\s|" + comment + + ")*+class(?:\\s|" + comment + + ")++" + Pattern.quote(name) + "\\b"); + Matcher m = p.matcher(s); + if (!m.find() || m.start() != 0) { return null; } + pos = offset + m.end() - 1; + } else { // generic class + JCTypeParameter param = cd.typarams.get(cd.typarams.length()-1); + int start = param.getEndPosition(tree.endPositions); + pos = getFirstInstanceAfter('>', start) + 1; + } + ((AnnotationInsertion) i).setGenerateExtends(true); + return pos; + } + + /** + * Returns the start position of the method's name. In particular, + * works properly for constructors, for which the name field in the + * AST is always "<init>" instead of the name from the source. + * + * @param node AST node of method declaration + * @return position of method name (from {@link JCMethodDecl#sym}) in source + */ + private int findMethodName(JCMethodDecl node) { + String sym = node.sym.toString(); + String name = sym.substring(0, sym.indexOf('(')); + JCModifiers mods = node.getModifiers(); + JCBlock body = node.body; + if ((mods.flags & Flags.GENERATEDCONSTR) != 0) { return Position.NOPOS; } + int nodeStart = node.getStartPosition(); + int nodeEnd = node.getEndPosition(tree.endPositions); + int nodeLength = nodeEnd - nodeStart; + int modsLength = mods.getEndPosition(tree.endPositions) + - mods.getStartPosition(); // can't trust string length! + int bodyLength = body == null ? 1 + : body.getEndPosition(tree.endPositions) - body.getStartPosition(); + int start = nodeStart + modsLength; + int end = nodeStart + nodeLength - bodyLength; + int angle = name.lastIndexOf('>'); // check for type params + if (angle >= 0) { name = name.substring(angle + 1); } + + try { + CharSequence s = tree.getSourceFile().getCharContent(true); + String regex = "\\b" + Pattern.quote(name) + "\\b"; // sufficient? + Pattern pat = Pattern.compile(regex, Pattern.MULTILINE); + Matcher mat = pat.matcher(s).region(start, end); + return mat.find() ? mat.start() : Position.NOPOS; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Determines if the annotation in the given insertion is already present + * at the given location in the AST. + * + * @param path the location in the AST to check for the annotation + * @param ins the annotation to check for + * @return {@code true} if the given annotation is already at the given + * location in the AST, {@code false} otherwise. + */ + private boolean alreadyPresent(TreePath path, Insertion ins) { + List<? extends AnnotationTree> alreadyPresent = null; + if (path != null) { + for (Tree n : path) { + if (n.getKind() == Tree.Kind.CLASS) { + alreadyPresent = ((ClassTree) n).getModifiers().getAnnotations(); + break; + } else if (n.getKind() == Tree.Kind.METHOD) { + alreadyPresent = ((MethodTree) n).getModifiers().getAnnotations(); + break; + } else if (n.getKind() == Tree.Kind.VARIABLE) { + alreadyPresent = ((VariableTree) n).getModifiers().getAnnotations(); + break; + } else if (n.getKind() == Tree.Kind.TYPE_CAST) { + Tree type = ((TypeCastTree) n).getType(); + if (type.getKind() == Tree.Kind.ANNOTATED_TYPE) { + alreadyPresent = ((AnnotatedTypeTree) type).getAnnotations(); + } + break; + } else if (n.getKind() == Tree.Kind.INSTANCE_OF) { + Tree type = ((InstanceOfTree) n).getType(); + if (type.getKind() == Tree.Kind.ANNOTATED_TYPE) { + alreadyPresent = ((AnnotatedTypeTree) type).getAnnotations(); + } + break; + } else if (n.getKind() == Tree.Kind.NEW_CLASS) { + JCNewClass nc = (JCNewClass) n; + if (nc.clazz.getKind() == Tree.Kind.ANNOTATED_TYPE) { + alreadyPresent = ((AnnotatedTypeTree) nc.clazz).getAnnotations(); + } + break; + } else if (n.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { + // If we pass through a parameterized type, stop, otherwise we + // mix up annotations on the outer type. + break; + } else if (n.getKind() == Tree.Kind.ARRAY_TYPE) { + Tree type = ((ArrayTypeTree) n).getType(); + if (type.getKind() == Tree.Kind.ANNOTATED_TYPE) { + alreadyPresent = ((AnnotatedTypeTree) type).getAnnotations(); + } + break; + } else if (n.getKind() == Tree.Kind.ANNOTATED_TYPE) { + alreadyPresent = ((AnnotatedTypeTree) n).getAnnotations(); + break; + } + // TODO: don't add cast insertion if it's already present. + } + } + + if (alreadyPresent != null) { + for (AnnotationTree at : alreadyPresent) { + // Compare the to-be-inserted annotation to the existing + // annotation, ignoring its arguments (duplicate annotations are + // never allowed even if they differ in arguments). If we did + // have to compare our arguments, we'd have to deal with enum + // arguments potentially being fully qualified or not: + // @Retention(java.lang.annotation.RetentionPolicy.CLASS) vs + // @Retention(RetentionPolicy.CLASS) + String ann = at.getAnnotationType().toString(); + // strip off leading @ along w/any leading or trailing whitespace + String text = ins.getText(); + String iann = Main.removeArgs(text).a.trim() + .substring(text.startsWith("@") ? 1 : 0); + String iannNoPackage = Insertion.removePackage(iann).b; + // System.out.printf("Comparing: %s %s %s%n", ann, iann, iannNoPackage); + if (ann.equals(iann) || ann.equals(iannNoPackage)) { + dbug.debug("Already present, not reinserting: %s%n", ann); + return true; + } + } + } + return false; + } + + /** + * Reports an error inserting an insertion to {@code System.err}. + * @param i the insertion that caused the error + * @param e the error. If there's a message it will be printed. + */ + public static void reportInsertionError(Insertion i, Throwable e) { + System.err.println("Error processing insertion:"); + System.err.println("\t" + i); + if (e.getMessage() != null) { + // If the message has multiple lines, indent them so it's easier to read. + System.err.println("\tError: " + e.getMessage().replace("\n", "\n\t\t")); + } + if (dbug.or(stak).isEnabled()) { + e.printStackTrace(); + } else { + System.err.println("\tRun with --print_error_stack to see the stack trace."); + } + System.err.println("\tThis insertion will be skipped."); + } + + /** + * Modifies the given receiver insertion so that it contains the type + * information necessary to insert a full method declaration receiver + * parameter. This is for receiver insertions where a receiver does not + * already exist in the source code. This will also add the annotations to be + * inserted to the correct part of the receiver type. + * + * @param path the location in the AST to insert the receiver + * @param receiver details of the receiver to insert + * @param method the method the receiver is being inserted into + */ + private void addReceiverType(TreePath path, ReceiverInsertion receiver, + MethodTree method) { + // Find the name of the class + // with type parameters to create the receiver. Walk up the tree and + // pick up class names to add to the receiver type. Since we're + // starting from the innermost class, the classes we get to at earlier + // iterations of the loop are inside of the classes we get to at later + // iterations. + TreePath parent = path; + Tree leaf = parent.getLeaf(); + Tree.Kind kind = leaf.getKind(); + // This is the outermost type, currently containing only the + // annotation to add to the receiver. + Type outerType = receiver.getType(); + DeclaredType baseType = receiver.getBaseType(); + // This holds the inner types as they're being read in. + DeclaredType innerTypes = null; + DeclaredType staticType = null; + // For an inner class constructor, the receiver comes from the + // superclass, so skip past the first type definition. + boolean isCon = ((MethodTree) parent.getLeaf()).getReturnType() == null; + boolean skip = isCon; + + while (kind != Tree.Kind.COMPILATION_UNIT + && kind != Tree.Kind.NEW_CLASS) { + if (kind == Tree.Kind.CLASS + || kind == Tree.Kind.INTERFACE + || kind == Tree.Kind.ENUM + || kind == Tree.Kind.ANNOTATION_TYPE) { + ClassTree clazz = (ClassTree) leaf; + String className = clazz.getSimpleName().toString(); + boolean isStatic = kind == Tree.Kind.INTERFACE + || kind == Tree.Kind.ENUM + || clazz.getModifiers().getFlags().contains(Modifier.STATIC); + skip &= !isStatic; + if (skip) { + skip = false; + receiver.setQualifyType(true); + } else if (!className.isEmpty()) { + // className will be empty for the CLASS node directly inside an + // anonymous inner class NEW_CLASS node. + DeclaredType inner = new DeclaredType(className); + if (staticType == null) { + // Only include type parameters on the classes to the right of and + // including the rightmost static class. + for (TypeParameterTree tree : clazz.getTypeParameters()) { + inner.addTypeParameter(new DeclaredType(tree.getName().toString())); + } + } + if (staticType == null && isStatic) { + // If this is the first static class then move the annotations here. + inner.setAnnotations(outerType.getAnnotations()); + outerType.clearAnnotations(); + staticType = inner; + } + if (innerTypes == null) { + // This is the first type we've read in, so set it as the + // innermost type. + innerTypes = inner; + } else { + // inner (the type just read in this iteration) is outside of + // innerTypes (the types already read in previous iterations). + inner.setInnerType(innerTypes); + innerTypes = inner; + } + } + } + parent = parent.getParentPath(); + leaf = parent.getLeaf(); + kind = leaf.getKind(); + } + if (isCon && innerTypes == null) { + throw new IllegalArgumentException( + "can't annotate (non-existent) receiver of non-inner constructor"); + } + + // Merge innerTypes into outerType: outerType only has the annotations + // on the receiver, while innerTypes has everything else. innerTypes can + // have the annotations if it is a static class. + baseType.setName(innerTypes.getName()); + baseType.setTypeParameters(innerTypes.getTypeParameters()); + baseType.setInnerType(innerTypes.getInnerType()); + if (staticType != null && !innerTypes.getAnnotations().isEmpty()) { + outerType.setAnnotations(innerTypes.getAnnotations()); + } + + Type type = (staticType == null) ? baseType : staticType; + Insertion.decorateType(receiver.getInnerTypeInsertions(), type, + receiver.getCriteria().getASTPath()); + + // If the method doesn't have parameters, don't add a comma. + receiver.setAddComma(method.getParameters().size() > 0); + } + + private void addNewType(TreePath path, NewInsertion neu, + NewArrayTree newArray) { + DeclaredType baseType = neu.getBaseType(); + if (baseType.getName().isEmpty()) { + List<String> annotations = neu.getType().getAnnotations(); + Type newType = Insertions.TypeTree.conv( + ((JCTree.JCNewArray) newArray).type); + for (String ann : annotations) { + newType.addAnnotation(ann); + } + neu.setType(newType); + } + Insertion.decorateType(neu.getInnerTypeInsertions(), neu.getType(), + neu.getCriteria().getASTPath()); + } + + private void addConstructor(TreePath path, ConstructorInsertion cons, + MethodTree method) { + ReceiverInsertion recv = cons.getReceiverInsertion(); + MethodTree leaf = (MethodTree) path.getLeaf(); + ClassTree parent = (ClassTree) path.getParentPath().getLeaf(); + DeclaredType baseType = cons.getBaseType(); + if (baseType.getName().isEmpty()) { + List<String> annotations = baseType.getAnnotations(); + String className = parent.getSimpleName().toString(); + Type newType = new DeclaredType(className); + cons.setType(newType); + for (String ann : annotations) { + newType.addAnnotation(ann); + } + } + if (recv != null) { + Iterator<Insertion> iter = cons.getInnerTypeInsertions().iterator(); + List<Insertion> recvInner = new ArrayList<Insertion>(); + addReceiverType(path, recv, leaf); + while (iter.hasNext()) { + Insertion i = iter.next(); + if (i.getCriteria().isOnReceiver()) { + recvInner.add(i); + iter.remove(); + } + } + Insertion.decorateType(recvInner, recv.getType(), + cons.getCriteria().getASTPath()); + } + Insertion.decorateType(cons.getInnerTypeInsertions(), cons.getType(), + cons.getCriteria().getASTPath()); + } + + public SetMultimap<ASTRecord, Insertion> getPaths() { + return Multimaps.unmodifiableSetMultimap(astInsertions); + } + + /** + * Scans the given tree with the given insertion list and returns the + * mapping from source position to insertion text. The positions are sorted + * in decreasing order of index, so that inserting one doesn't throw + * off the index for a subsequent one. + * + * <p> + * <i>N.B.:</i> This method calls {@code scan()} internally. + * </p> + * + * @param node the tree to scan + * @param p the list of insertion criteria + * @return the source position to insertion text mapping + */ + public SetMultimap<Pair<Integer, ASTPath>, Insertion> + getInsertionsByPosition(JCCompilationUnit node, List<Insertion> p) { + List<Insertion> uninserted = new ArrayList<Insertion>(p); + this.scan(node, uninserted); + // There may be many extra annotations in a .jaif file. For instance, + // the .jaif file may be for an entire library, but its compilation + // units are processed one by one. + // However, we should warn about any insertions that were within the + // given compilation unit but still didn't get inserted. + List<? extends Tree> typeDecls = node.getTypeDecls(); + for (Insertion i : uninserted) { + InClassCriterion c = i.getCriteria().getInClass(); + if (c == null) { + continue; + } + for (Tree t : typeDecls) { + if (c.isSatisfiedBy(TreePath.getPath(node, t))) { + // Avoid warnings about synthetic generated methods. + // This test is too coarse, but is good enough for now. + // There are also synthetic local variables; maybe suppress + // warnings about them, too. + if (! (i.getCriteria().isOnMethod("<init>()V") + || i.getCriteria().isOnLocalVariable())) { + // Should be made more user-friendly + System.err.printf("Found class %s, but unable to insert %s:%n %s%n", c.className, i.getText(), i); + } + } + } + } + if (dbug.isEnabled()) { + // Output every insertion that was not given a position: + for (Insertion i : uninserted) { + System.err.println("Unable to insert: " + i); + } + } + dbug.debug("getPositions => %d positions%n", insertions.size()); + return Multimaps.unmodifiableSetMultimap(insertions); + } + + /** + * Scans the given tree with the given {@link Insertions} and returns + * the mapping from source position to insertion text. + * + * <p> + * <i>N.B.:</i> This method calls {@code scan()} internally. + * </p> + * + * @param node the tree to scan + * @param insertions the insertion criteria + * @return the source position to insertion text mapping + */ + public SetMultimap<Pair<Integer, ASTPath>, Insertion> + getPositions(JCCompilationUnit node, Insertions insertions) { + List<Insertion> list = new ArrayList<Insertion>(); + treePathCache.clear(); + list.addAll(insertions.forOuterClass(node, "")); + for (JCTree decl : node.getTypeDecls()) { + if (decl.getTag() == JCTree.Tag.CLASSDEF) { + String name = ((JCClassDecl) decl).sym.className(); + Collection<Insertion> forClass = insertions.forOuterClass(node, name); + list.addAll(forClass); + } + } + return getInsertionsByPosition(node, list); + } +}
diff --git a/annotation-file-utilities/src/annotator/find/TypeArgumentCriterion.java b/annotation-file-utilities/src/annotator/find/TypeArgumentCriterion.java new file mode 100644 index 0000000..ef5bb44 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/TypeArgumentCriterion.java
@@ -0,0 +1,62 @@ +package annotator.find; + +import java.util.List; + +import annotations.el.RelativeLocation; + +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.tree.JCTree; + +public class TypeArgumentCriterion implements Criterion { + private final String methodName; + private final RelativeLocation loc; + + public TypeArgumentCriterion(String methodName, RelativeLocation loc) { + this.methodName = methodName; + this.loc = loc; + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path, Tree leaf) { + assert path == null || path.getLeaf() == leaf; + return isSatisfiedBy(path); + } + + /** {@inheritDoc} */ + @Override + public boolean isSatisfiedBy(TreePath path) { + if (path == null || path.getParentPath() == null) { return false; } + + TreePath parentPath = path.getParentPath(); + Tree parent = parentPath.getLeaf(); + List<? extends Tree> typeArgs; + + switch (parent.getKind()) { + case MEMBER_REFERENCE: + typeArgs = ((JCTree.JCMemberReference) parent).getTypeArguments(); + break; + case METHOD_INVOCATION: + typeArgs = ((JCTree.JCMethodInvocation) parent).getTypeArguments(); + break; + default: + return isSatisfiedBy(parentPath); + } + + return typeArgs != null + && loc.index >= 0 && loc.index < typeArgs.size() + && typeArgs.get(loc.index) == path.getLeaf(); + } + + @Override + public Kind getKind() { + return Kind.TYPE_ARGUMENT; + } + + @Override + public String toString() { + return "TypeArgumentCriterion: in method: " + methodName + + " location: " + loc; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/TypeBoundExtendsInsertion.java b/annotation-file-utilities/src/annotator/find/TypeBoundExtendsInsertion.java new file mode 100644 index 0000000..0615a25 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/TypeBoundExtendsInsertion.java
@@ -0,0 +1,41 @@ +package annotator.find; + +/** + * Specifies an insertion of an "extends @Annotation java.lang.Object" to a type + * bound. + */ +public class TypeBoundExtendsInsertion extends AnnotationInsertion { + + /** + * Creates a new TypeBoundExtendsInsertion. + * + * @param text + * the text to insert + * @param criteria + * where to insert the text + * @param separateLine + * whether to insert the text on its own + */ + public TypeBoundExtendsInsertion(String text, Criteria criteria, + boolean separateLine) { + super(text, criteria, separateLine); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getText(boolean comments, boolean abbreviate) { + return "extends java.lang." + super.getText(comments, abbreviate) + + " Object"; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean addTrailingSpace(boolean gotSeparateLine) { + // Never add a trailing space for an extends insertion. + return false; + } +}
diff --git a/annotation-file-utilities/src/annotator/find/TypedInsertion.java b/annotation-file-utilities/src/annotator/find/TypedInsertion.java new file mode 100644 index 0000000..2636e1b --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/TypedInsertion.java
@@ -0,0 +1,107 @@ +package annotator.find; + +import java.util.List; + +import type.ArrayType; +import type.BoundedType; +import type.DeclaredType; +import type.Type; + +/** + * Superclass for {@link Insertion} classes for which insertion may + * result in code generation other than just annotations. + * {@code TypedInsertion}s keep track of insertions on inner types. If + * there is no type given in the source, one may be generated (along + * with other code necessary in the context) to serve as an insertion + * site. + * <p> + * We don't know until the end of the whole insertion process whether + * the type already exists or not. To remedy this, we store a reference + * to each insertion on an inner type of a receiver in two places: the + * global list of all insertions and the {@code TypedInsertion} that is + * the parent of the inner type insertion. If the type is not already + * present, the inner type insertions are inserted into the new type and + * labeled as "inserted" (with {@link Insertion#setInserted(boolean)}) + * so they are not inserted as the rest of the insertions list is + * processed. + * + * @author dbro + */ +public abstract class TypedInsertion extends Insertion { + /** + * The type for insertion. + */ + protected Type type; + + /** + * If true only the annotations from {@link type} will be inserted. + */ + protected boolean annotationsOnly; + + /** + * The inner types to go on this insertion. See {@link ReceiverInsertion} + * for more details. + */ + protected List<Insertion> innerTypeInsertions; + + public TypedInsertion(Type type, Criteria criteria, + List<Insertion> innerTypeInsertions) { + this(type, criteria, false, innerTypeInsertions); + } + + public TypedInsertion(Type type, Criteria criteria, boolean b, + List<Insertion> innerTypeInsertions) { + super(criteria, b); + this.type = type; + this.innerTypeInsertions = innerTypeInsertions; + annotationsOnly = false; + } + + /** + * If {@code true} only the annotations on {@code type} will be inserted. + * This is useful when the "new" has already been inserted. + */ + public void setAnnotationsOnly(boolean annotationsOnly) { + this.annotationsOnly = annotationsOnly; + } + + /** + * Sets the type. + */ + public void setType(Type type) { + this.type = type; + } + + /** + * Gets the type. It is assumed that the returned value will be + * modified to update the type to be inserted. + */ + public Type getType() { + return type; + } + + /** + * Gets the inner type insertions associated with this insertion. + * @return a copy of the inner types + */ + public List<Insertion> getInnerTypeInsertions() { + return innerTypeInsertions; + } + + public DeclaredType getBaseType() { + return getBaseType(type); + } + + public static DeclaredType getBaseType(Type type) { + switch (type.getKind()) { + case DECLARED: + return (DeclaredType) type; + case BOUNDED: + return getBaseType(((BoundedType) type).getName()); + case ARRAY: + return getBaseType(((ArrayType) type).getComponentType()); + default: // should never be reached + return null; + } + } +}
diff --git a/annotation-file-utilities/src/annotator/find/package-info.java b/annotation-file-utilities/src/annotator/find/package-info.java new file mode 100644 index 0000000..7e60dd8 --- /dev/null +++ b/annotation-file-utilities/src/annotator/find/package-info.java
@@ -0,0 +1,21 @@ +/** + * Provides interfaces and classes for finding where in the existing + * source to insert annotations etc. {@link annotator.find.TreeFinder} manages the + * control flow and discovers the positions for insertion, relying on + * implementations of {@link annotator.find.Criterion} for determining whether an + * insertion should be made at a given location and on extensions of + * {@link annotator.find.Insertion} for the concrete text to be inserted. + * <p> + * The current flow, given a collection of insertions and an abstract + * syntax tree (AST) representing a Java source file, consists of a + * pre-order traversal of the AST to find insertion positions, followed + * by the insertion of text for each positioned {@link annotator.find.Insertion} into + * the source code, in reverse order by position. At each annotatable + * node encountered during the traversal, the program checks the + * {@link annotator.find.Criteria} for each yet-unmatched {@link annotator.find.Insertion} against + * the current node; when there is a match, the program finds and + * records the appropriate source position. + * + * @see annotator.find.TreeFinder#getInsertionsByPosition(com.sun.tools.javac.tree.JCTree.JCCompilationUnit, java.util.List) + */ +package annotator.find;
diff --git a/annotation-file-utilities/src/annotator/scanner/AnonymousClassScanner.java b/annotation-file-utilities/src/annotator/scanner/AnonymousClassScanner.java new file mode 100644 index 0000000..13d5afd --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/AnonymousClassScanner.java
@@ -0,0 +1,105 @@ +package annotator.scanner; + +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; + +/** + * AnonymousClassScanner determine the index of a tree for an anonymous + * class. If the index is i, it is the ith anonymous class in the file. + * Thus, if i = 2, it will have a name of the form NamedClass$2. + */ +public class AnonymousClassScanner extends TreePathScanner<Void, Integer> { + + /** + * Given an anonymous class, computes and returns its 1-based index in the given tree representing + * an anonymous class. + * + * @param path the source path ending in the anonymous class + * @param anonclass the anonymous class to search for + * @return the index of the anonymous class in the source code + */ + public static int indexOfClassTree(TreePath path, Tree anonclass) { + // Move up to the CLASS tree enclosing this CLASS tree and start the tree + // traversal from there. This prevents us from counting anonymous classes + // that are in a different part of the tree and therefore aren't included + // in the index number. + int classesFound = 0; + boolean anonclassFound = false; + while (path.getParentPath() != null && classesFound < 1) { + if (path.getLeaf() == anonclass) { + anonclassFound = true; + } + path = path.getParentPath(); + if (anonclassFound && CommonScanner.hasClassKind(path.getLeaf())) { + classesFound++; + } + } + AnonymousClassScanner lvts = new AnonymousClassScanner(anonclass); + lvts.scan(path, 0); + if (lvts.found) { + return lvts.index; + } else { + return -1; + } + } + + private int index; + // top-level class doesn't count, so first index will be -1 + private boolean found; + private Tree anonclass; + + /** + * Creates a new AnonymousClassScanner that searches for the index of the given + * tree, representing an anonymous class. + * + * @param tree the anonymous class to search for + */ + private AnonymousClassScanner(Tree anonclass) { + this.index = 1; // start counting at 1 + this.found = false; + this.anonclass = anonclass; + } + + // Slightly tricky counting: if the target item is a CLASS, only count + // CLASSes. If it is a NEW_CLASS, only count NEW_CLASSes + // The level parameter keeps us from traversing too low in the tree and + // counting classes that aren't included in the index number. + + @Override + public Void visitClass(ClassTree node, Integer level) { + if (level < 2) { + if (!found && CommonScanner.hasClassKind(anonclass)) { + if (anonclass == node) { + found = true; + } else if (node.getSimpleName().toString().trim().isEmpty()) { + // don't count classes with given names in source + index++; + } + } + super.visitClass(node, level + 1); + } + return null; + } + + @Override + public Void visitNewClass(NewClassTree node, Integer level) { + // if (level < 2) { + if (!found && anonclass.getKind() == Tree.Kind.NEW_CLASS) { + if (anonclass == node) { + found = true; + } else if (node.getClassBody() != null) { + // Need to make sure you actually are creating anonymous inner class, + // not just object creation. + index++; + } else { + return null; + } + } + super.visitNewClass(node, level + 1); + // } + return null; + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/CastScanner.java b/annotation-file-utilities/src/annotator/scanner/CastScanner.java new file mode 100644 index 0000000..428e538 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/CastScanner.java
@@ -0,0 +1,115 @@ +package annotator.scanner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.source.tree.Tree; +import com.sun.source.tree.TypeCastTree; +import com.sun.source.util.TreePath; + +/** CastScanner stores information about the names and offsets of + * casts inside a method, and can also be used to scan the source + * tree and determine the index of a given cast, where the i^th + * index corresponds to the i^th cast, using 0-based indexing. + */ +public class CastScanner extends CommonScanner { + + /** + * Computes the index of the given cast tree amongst all cast trees inside + * its method, using 0-based indexing. + * + * @param origpath the path ending in the given cast tree + * @param tree the cast tree to search for + * @return the index of the given cast tree + */ + public static int indexOfCastTree(TreePath origpath, Tree tree) { + TreePath path = findCountingContext(origpath); + if (path == null) { + return -1; + } + + CastScanner lvts = new CastScanner(tree); + lvts.scan(path, null); + return lvts.index; + } + + private int index = -1; + private boolean done = false; + private final Tree tree; + private static String prevMethodName = null; + private static int prevOffset = -1; + private static int nestLevels = 0; + + private CastScanner(Tree tree) { + this.index = -1; + this.done = false; + this.tree = tree; + } + + @Override + public Void visitTypeCast(TypeCastTree node, Void p) { + if (!done) { + index++; + } + if (tree == node) { + done = true; + return p; + } + return super.visitTypeCast(node, p); + } + + // Map from name of a method a list of bytecode offsets of all + // casts in that method. + private static Map<String,List<Integer>> methodNameToCastOffsets = + new HashMap<String, List<Integer>>(); + + + /** + * Adds a cast bytecode offset to the current list of offsets for + * methodName. This method must be called with monotonically increasing + * offsets for any one method. + * + * @param methodName the name of the method + * @param offset the offset to add + */ + public static void addCastToMethod(String methodName, Integer offset) { + List< Integer> offsetList = methodNameToCastOffsets.get(methodName); + if (offsetList == null) { + offsetList = new ArrayList< Integer>(); + methodNameToCastOffsets.put(methodName, offsetList); + } + if (methodName.equals(prevMethodName) && offset-prevOffset == 3) { + // consecutive instructions -> nested casts -> reverse order! + // TODO: other cases for nested casts? + ++nestLevels; + offsetList.add(offsetList.size()-nestLevels, offset); + } else { + nestLevels = 0; + offsetList.add(offset); + } + prevMethodName = methodName; + prevOffset = offset; + } + + /** + * Returns the index of the given offset within the list of offsets + * for the given method, using 0-based indexing, + * or returns a negative number if the offset is not one of the + * offsets in the method. + * + * @param methodName the name of the method + * @param offset the offset of the instanceof check + * @return the index of the given offset, or a negative number if the + * given offset does not exists inside the method + */ + public static Integer getMethodCastIndex(String methodName, Integer offset) { + List<Integer> offsetList = methodNameToCastOffsets.get(methodName); + if (offsetList == null) { + return -1; + } + + return offsetList.indexOf(offset); + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/CommonScanner.java b/annotation-file-utilities/src/annotator/scanner/CommonScanner.java new file mode 100644 index 0000000..0f65b8b --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/CommonScanner.java
@@ -0,0 +1,143 @@ +package annotator.scanner; + +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; + +/** + * The common base-class for all scanners that contains shared tree-traversal + * methods. + */ +public class CommonScanner extends TreePathScanner<Void, Void> { + public static boolean hasClassKind(Tree tree) { + Tree.Kind kind = tree.getKind(); + // Tree.Kind.NEW_CLASS is excluded here because 1) there is no + // type name to be annotated on an anonymous inner class, and + // consequently 2) NEW_CLASS insertions are handled separately. + return kind == Tree.Kind.CLASS + || kind == Tree.Kind.INTERFACE + || kind == Tree.Kind.ENUM + || kind == Tree.Kind.ANNOTATION_TYPE; + } + + /** + * The counting context for new, typecast, instanceof, and locals. + * This is a path to a method or a field/instance/static initializer. + */ + public static TreePath findCountingContext(TreePath path) { + while (path != null) { + if (path.getLeaf().getKind() == Tree.Kind.METHOD || + isFieldInit(path) || + isInitBlock(path)) { + return path; + } + path = path.getParentPath(); + } + return path; + } + + // classes + + public static TreePath findEnclosingClass(TreePath path) { + while (!hasClassKind(path.getLeaf()) + || path.getParentPath().getLeaf().getKind() == + Tree.Kind.NEW_CLASS) { + path = path.getParentPath(); + if (path == null) { + return null; + } + } + return path; + } + + // methods + + public static TreePath findEnclosingMethod(TreePath path) { + while (path.getLeaf().getKind() != Tree.Kind.METHOD) { + path = path.getParentPath(); + if (path == null) { + return null; + } + } + return path; + } + + // Field Initializers + + public static boolean isFieldInit(TreePath path) { + return path.getLeaf().getKind() == Tree.Kind.VARIABLE + && path.getParentPath() != null + && hasClassKind(path.getParentPath().getLeaf()); + } + + public static TreePath findEnclosingFieldInit(TreePath path) { + while (!isFieldInit(path)) { + path = path.getParentPath(); + if (path == null) { + return null; + } + } + return path; + } + + // initializer blocks + + public static boolean isInitBlock(TreePath path, boolean isStatic) { + return isInitBlock(path) + && ((BlockTree) path.getLeaf()).isStatic() == isStatic; + } + + public static boolean isInitBlock(TreePath path) { + return path.getParentPath() != null + && hasClassKind(path.getParentPath().getLeaf()) + && path.getLeaf().getKind() == Tree.Kind.BLOCK; + } + + public static TreePath findEnclosingInitBlock(TreePath path, + boolean isStatic) { + while (!isInitBlock(path, isStatic)) { + path = path.getParentPath(); + if (path == null) { + return null; + } + } + return path; + } + + public static boolean isStaticInit(TreePath path) { + return isInitBlock(path, true); + } + + public static TreePath findEnclosingStaticInit(TreePath path) { + while (!isStaticInit(path)) { + path = path.getParentPath(); + if (path == null) { + return null; + } + } + return path; + } + + public static boolean isInstanceInit(TreePath path) { + return isInitBlock(path, false); + } + + public static TreePath findEnclosingInstanceInit(TreePath path) { + while (!isInstanceInit(path)) { + path = path.getParentPath(); + if (path == null) { + return null; + } + } + return path; + } + + // Don't scan into any classes so that occurrences in nested classes + // aren't counted. + @Override + public Void visitClass(ClassTree node, Void p) { + return p; + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/InitBlockScanner.java b/annotation-file-utilities/src/annotator/scanner/InitBlockScanner.java new file mode 100644 index 0000000..f02a07e --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/InitBlockScanner.java
@@ -0,0 +1,56 @@ +package annotator.scanner; + +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; +import com.sun.tools.javac.tree.JCTree.JCBlock; + +/** + * InitScanner scans the source tree and determines the index of a given + * initializer block, where index {@code i} corresponds to the (0-based) + * i^th initializer of the indicated kind (static or instance), + * + * @author dbro + */ +public class InitBlockScanner extends TreePathScanner<Void, Boolean> { + public static int indexOfInitTree(TreePath path, boolean isStatic) { + // we allow to start with any path/tree within an initializer. + // first go to the enclosing initializer + Tree tree = + CommonScanner.findEnclosingInitBlock(path, isStatic).getLeaf(); + // find the enclosing class + path = CommonScanner.findEnclosingClass(path); + if (tree==null || path == null) { + return -1; + } + // find the index of the current initializer within the + // enclosing class + InitBlockScanner bts = new InitBlockScanner(tree); + bts.scan(path, isStatic); + return bts.index; + } + + private int index = -1; + private boolean done = false; + private final Tree tree; + + private InitBlockScanner(Tree tree) { + this.index = -1; + this.done = false; + this.tree = tree; + } + + @Override + public Void visitBlock(BlockTree node, Boolean isStatic) { + // TODO: is isStatic only used for static initializer blocks? + if (!done && isStatic == node.isStatic() + && ((JCBlock) node).endpos >= 0) { + index++; + } + if (tree == node) { + done = true; + } + return super.visitBlock(node, isStatic); + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/InstanceOfScanner.java b/annotation-file-utilities/src/annotator/scanner/InstanceOfScanner.java new file mode 100644 index 0000000..f6acd78 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/InstanceOfScanner.java
@@ -0,0 +1,105 @@ +package annotator.scanner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.source.tree.InstanceOfTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** InstanceOfScanner stores information about the names and offsets of + * instanceof checks inside a method, and can also be used to scan the source + * tree and determine the index of a given instanceof check, where the i^th + * index corresponds to the i^th instanceof check, using 0-based indexing. + */ +public class InstanceOfScanner extends CommonScanner { + + /** + * Computes the index of the given instanceof tree amongst all instanceof + * tree inside its method, using 0-based indexing. + * + * @param origpath the path ending in the given instanceof tree + * @param tree the instanceof tree to search for + * @return the index of the given instanceof tree + */ + public static int indexOfInstanceOfTree(TreePath origpath, Tree tree) { + TreePath path = findCountingContext(origpath); + if (path == null) { + return -1; + } + + InstanceOfScanner ios = new InstanceOfScanner(tree); + ios.scan(path, null); + return ios.index; + } + + private int index = -1; + private boolean done = false; + private final Tree tree; + + /** + * Creates an InstanceOfScanner that will scan the source tree for the + * given node representing the instanceof check to find. + * @param tree the given instanceof check to search for + */ + private InstanceOfScanner(Tree tree) { + this.index = -1; + this.done = false; + this.tree = tree; + } + + @Override + public Void visitInstanceOf(InstanceOfTree node, Void p) { + if (!done) { + index++; + } + if (tree == node) { + done = true; + } + return super.visitInstanceOf(node, p); + } + + // Map from name of a method to a list of bytecode offsets of all + // instanceof checks in that method. + private static Map<String, List<Integer>> methodNameToInstanceOfOffsets = + new HashMap<String, List<Integer>>(); + + /** + * Adds an instanceof bytecode offset to the current list of offsets for + * methodName. This method must be called with monotonically increasing + * offsets for any one method. + * + * @param methodName the name of the method + * @param offset the offset to add + */ + public static void addInstanceOfToMethod(String methodName, Integer offset) { + List< Integer> offsetList = methodNameToInstanceOfOffsets.get(methodName); + if (offsetList == null) { + offsetList = new ArrayList< Integer>(); + methodNameToInstanceOfOffsets.put(methodName, offsetList); + } + offsetList.add(offset); + } + + /** + * Returns the index of the given offset within the list of offsets + * for the given method, using 0-based indexing, + * or returns a negative number if the offset is not one of the + * offsets in the method. + * + * @param methodName the name of the method + * @param offset the offset of the instanceof check + * @return the index of the given offset, or a negative number + * if the offset does not exist inside the method + */ + public static Integer getMethodInstanceOfIndex(String methodName, Integer offset) { + List< Integer> offsetList = methodNameToInstanceOfOffsets.get(methodName); + if (offsetList == null) { + return -1; + } + + return offsetList.indexOf(offset); + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/LambdaScanner.java b/annotation-file-utilities/src/annotator/scanner/LambdaScanner.java new file mode 100644 index 0000000..58f6e45 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/LambdaScanner.java
@@ -0,0 +1,109 @@ +package annotator.scanner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.source.tree.LambdaExpressionTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +/** + * LambdaScanner stores information about the names and offsets of + * lambda expressions inside a method, and can also be used to scan the + * source tree and determine the index of a given instanceof check, + * where the i^th index corresponds to the i^th instanceof check, using + * 0-based indexing. + */ +public class LambdaScanner extends CommonScanner { + + /** + * Computes the index of the given lambda expression tree amongst all + * lambda expression trees inside its method, using 0-based indexing. + * + * @param origpath the path ending in the given lambda expression tree + * @param tree the lambda expression tree to search for + * @return the index of the given lambda expression tree + */ + public static int indexOfLambdaExpressionTree(TreePath origpath, Tree tree) { + TreePath path = findCountingContext(origpath); + if (path == null) { + return -1; + } + + LambdaScanner ls = new LambdaScanner(tree); + ls.scan(path, null); + return ls.index; + } + + private int index; + private boolean done; + private final Tree tree; + + /** + * Creates an InstanceOfScanner that will scan the source tree for the + * given node representing the lambda expression to find. + * @param tree the given lambda expression to search for + */ + private LambdaScanner(Tree tree) { + this.index = -1; + this.done = false; + this.tree = tree; + } + + @Override + public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { + if (!done) { + index++; + } + if (tree == node) { + done = true; + } + return super.visitLambdaExpression(node, p); + } + + // Map from name of a method to a list of bytecode offsets of all + // lambda expressions in that method. + private static Map<String, List<Integer>> methodNameToLambdaExpressionOffsets = + new HashMap<String, List<Integer>>(); + + /** + * Adds a lambda expression bytecode offset to the current list of + * offsets for methodName. This method must be called with + * monotonically increasing offsets for any one method. + * + * @param methodName the name of the method + * @param offset the offset to add + */ + public static void addLambdaExpressionToMethod(String methodName, Integer offset) { + List<Integer> offsetList = + methodNameToLambdaExpressionOffsets.get(methodName); + if (offsetList == null) { + offsetList = new ArrayList<Integer>(); + methodNameToLambdaExpressionOffsets.put(methodName, offsetList); + } + offsetList.add(offset); + } + + /** + * Returns the index of the given offset within the list of offsets + * for the given method, using 0-based indexing, or returns a negative + * number if the offset is not one of the offsets in the context. + * + * @param methodName the name of the method + * @param offset the offset of the lambda expression + * @return the index of the given offset, or a negative number + * if the offset does not exist inside the context + */ + public static Integer + getMethodLambdaExpressionIndex(String methodName, Integer offset) { + List<Integer> offsetList = + methodNameToLambdaExpressionOffsets.get(methodName); + if (offsetList == null) { + return -1; + } + + return offsetList.indexOf(offset); + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/LocalClassScanner.java b/annotation-file-utilities/src/annotator/scanner/LocalClassScanner.java new file mode 100644 index 0000000..d1ef277 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/LocalClassScanner.java
@@ -0,0 +1,89 @@ +package annotator.scanner; + +import com.sun.source.tree.BlockTree; +import com.sun.source.tree.ClassTree; +import com.sun.source.tree.StatementTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; +import com.sun.source.util.TreePathScanner; + +/** + * LocalClassScanner determines the index of a tree for a local + * class. If the index is i, it is the ith local class with the class name in + * the file. Thus, if i = 2, it will have a name of the form + * OuterClass$2InnerClass. + */ +public class LocalClassScanner extends TreePathScanner<Void, Integer> { + + /** + * Given a local class, computes and returns its 1-based index in the given + * tree representing a local class. + * + * @param path the source path ending in the local class + * @param localClass the local class to search for + * @return the index of the local class in the source code + */ + public static int indexOfClassTree(TreePath path, ClassTree localClass) { + // Move up to the CLASS tree enclosing this CLASS tree and start the tree + // traversal from there. This prevents us from counting local classes that + // are in a different part of the tree and therefore aren't included in the + // index number. + int classesFound = 0; + boolean localClassFound = false; + while (path.getParentPath() != null && classesFound < 1) { + if (path.getLeaf() == localClass) { + localClassFound = true; + } + path = path.getParentPath(); + if (localClassFound && path.getLeaf().getKind() == Tree.Kind.CLASS) { + classesFound++; + } + } + LocalClassScanner lcs = new LocalClassScanner(localClass); + lcs.scan(path, 0); + if (lcs.found) { + return lcs.index; + } else { + return -1; + } + } + + private int index; + private boolean found; + private ClassTree localClass; + + /** + * Creates a new LocalClassScanner that searches for the index of the given + * tree, representing a local class. + * + * @param localClass the local class to search for + */ + private LocalClassScanner(ClassTree localClass) { + this.index = 1; + this.found = false; + this.localClass = localClass; + } + + // The level parameter keeps us from traversing too low in the tree and + // counting classes that aren't included in the index number. + + @Override + public Void visitBlock(BlockTree node, Integer level) { + if (level < 1) { + // Visit blocks since a local class can only be in a block. Then visit each + // statement of the block to see if any are the correct local class. + for (StatementTree statement : node.getStatements()) { + if (!found && statement.getKind() == Tree.Kind.CLASS) { + ClassTree c = (ClassTree) statement; + if (localClass == statement) { + found = true; + } else if (c.getSimpleName().equals(localClass.getSimpleName())) { + index++; + } + } + } + super.visitBlock(node, level + 1); + } + return null; + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/LocalVariableScanner.java b/annotation-file-utilities/src/annotator/scanner/LocalVariableScanner.java new file mode 100644 index 0000000..30b6c15 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/LocalVariableScanner.java
@@ -0,0 +1,167 @@ +package annotator.scanner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.source.tree.Tree; +import com.sun.source.tree.VariableTree; +import com.sun.source.util.TreePath; +import com.sun.tools.javac.util.Pair; + +/** LocalVariableScanner stores information about the names and offsets of + * local variables inside a method, and can also be used to scan the source + * tree and determine the index of a local variable with a given name, so that + * the i^th index corresponds to the i^th declaration of a local variable with + * that name, using 0-based indexing. + */ +public class LocalVariableScanner extends CommonScanner { + /** + * Computes the index i of the given tree along the given tree path + * such that it is the i^th declaration of the local variable with the given + * var name, using 0-based indexing. + * + * @param origpath the source path that ends in varTree + * @param varTree the variable tree that declares the local variable + * @param varName the name of the local variable + * @return the index of the variable tree with respect to the given + * local variable name + */ + public static int indexOfVarTree(TreePath origpath, Tree varTree, String varName) { + TreePath path = findCountingContext(origpath); + if (path == null) { + return -1; + } + + LocalVariableScanner lvts = new LocalVariableScanner(varTree, varName); + + try { + lvts.scan(path, null); + } catch (Throwable e) { + System.out.println("LocalVariableScanner: can't locate: " + varTree); + return -2; // Don't return -1, which is above return code + } + return lvts.index; + } + + /* + * For efficiency, we might want to have methods like the following, specialized for the + * three different cases. + */ + /* + public static int indexOfVarTreeInStaticInit(TreePath path, Tree tree, String varName) { + // only start searching from within this method + path = findEnclosingStaticInit(path); + if (path == null) { + return -1; + } + + LocalVariableScanner lvts = new LocalVariableScanner(tree, varName); + lvts.scan(path, null); + return lvts.index; + } + */ + + private int index = -1; + private boolean done = false; + private final Tree varTree; + private final String varName; + + private LocalVariableScanner(Tree varTree, String varName) { + this.index = -1; + this.done = false; + this.varTree = varTree; + this.varName = varName; + } + + @Override + public Void visitVariable(VariableTree node, Void p) { + // increment index only if you have not already reached the right node, and + // if this node declares the same local variable you are searching for + if (varName.equals(node.getName().toString())) { + if (!done) { + index++; + } + if (varTree == node) { + done = true; + } + } + return p; + } + + // TODO: refactor class keys to avoid so many uses of generics + + // mapping from (method-name, variable-index, start-offset) + // to variable name + private static Map<Pair<String, Pair<Integer,Integer>>, String> + methodNameIndexMap = new HashMap<Pair<String, Pair<Integer,Integer>>, String>(); + + // map from method to map from variable name to + // a list of start offsets + private static Map<String, Map<String, List<Integer>>> + methodNameCounter = new HashMap<String, Map<String,List<Integer>>>(); + + /** + * Adds the given variable specified as a pair of method name and + * (index, start-offset) under the given name to the list of all local + * variables. + * + * @param varInfo a pair of the method and a pair describing the local + * variable index and start offset of the local variable + * @param name the name of the local variable + */ + public static void addToMethodNameIndexMap(Pair<String, Pair<Integer,Integer>> varInfo, String name) { + methodNameIndexMap.put(varInfo, name); + } + + /** + * Gets the name of the local variable in the given method, and at the + * the given index and offset. + * + * @param varInfo a pair of the method name and a pair of the local variable's + * index and start offset + * @return the name of the local variable at the specified location + */ + public static String getFromMethodNameIndexMap(Pair<String, Pair<Integer, Integer>> varInfo) { + return methodNameIndexMap.get(varInfo); + } + + /** + * Adds to the given method the fact that the local variable with the given + * name is declared at the given start offset. + * + * @param methodName the method containing the local variable + * @param varName the name of the local variable + * @param offset the start offset of the local variable + */ + public static void addToMethodNameCounter(String methodName, String varName, + Integer offset) { + Map<String, List<Integer>> nameOffsetCounter = methodNameCounter.get(methodName); + if (nameOffsetCounter == null) { + nameOffsetCounter = new HashMap<String, List<Integer>>(); + methodNameCounter.put(methodName, nameOffsetCounter); + } + + List< Integer> listOfOffsets = nameOffsetCounter.get(varName); + if (listOfOffsets == null) { + listOfOffsets = new ArrayList< Integer>(); + nameOffsetCounter.put(varName, listOfOffsets); + } + + listOfOffsets.add(offset); + } + + /** + * Returns a list of all start bytecode offsets of variable declarations with + * the given variable name in the given method. + * + * @param methodName the name of the method + * @param varName the name of the local variable + * @return a list of start offsets for live ranges of all local variables + * with the given name in the given method + */ + public static List<Integer> getFromMethodNameCounter(String methodName, String varName) { + return methodNameCounter.get(methodName).get(varName); + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/MemberReferenceScanner.java b/annotation-file-utilities/src/annotator/scanner/MemberReferenceScanner.java new file mode 100644 index 0000000..c3cbf44 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/MemberReferenceScanner.java
@@ -0,0 +1,102 @@ +package annotator.scanner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.source.tree.MemberReferenceTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class MemberReferenceScanner extends CommonScanner { + + /** + * Computes the index of the given method invocation amongst all + * method invocation trees inside its method, using 0-based indexing. + * + * @param origpath the path ending in the given method invocation tree + * @param tree the method invocation tree to search for + * @return the index of the given method invocation tree + */ + public static int indexOfMemberReferenceTree(TreePath origpath, Tree tree) { + TreePath path = findCountingContext(origpath); + if (path == null) { + return -1; + } + + MemberReferenceScanner mcs = new MemberReferenceScanner(tree); + mcs.scan(path, null); + return mcs.index; + } + + private int index; + private boolean done; + private final Tree tree; + + /** + * Creates an InstanceOfScanner that will scan the source tree for the + * given node representing the method invocation to find. + * @param tree the given method invocation to search for + */ + private MemberReferenceScanner(Tree tree) { + this.index = -1; + this.done = false; + this.tree = tree; + } + + @Override + public Void visitMemberReference(MemberReferenceTree node, Void p) { + if (!done) { + index++; + } + if (tree == node) { + done = true; + } + return super.visitMemberReference(node, p); + } + + // Map from name of a method to a list of bytecode offsets of all + // lambda expressions in that method. + private static Map<String, List<Integer>> methodNameToMemberReferenceOffsets = + new HashMap<String, List<Integer>>(); + + /** + * Adds a member reference bytecode offset to the current list of + * offsets for methodName. This method must be called with + * monotonically increasing offsets for any one method. + * + * @param methodName the name of the method + * @param offset the offset to add + */ + public static void addMemberReferenceToMethod(String methodName, Integer offset) { + List<Integer> offsetList = + methodNameToMemberReferenceOffsets.get(methodName); + if (offsetList == null) { + offsetList = new ArrayList<Integer>(); + methodNameToMemberReferenceOffsets.put(methodName, offsetList); + } + offsetList.add(offset); + } + + /** + * Returns the index of the given offset within the list of offsets + * for the given method, using 0-based indexing, or returns a negative + * number if the offset is not one of the offsets in the context. + * + * @param methodName the name of the method + * @param offset the offset of the lambda expression + * @return the index of the given offset, or a negative number + * if the offset does not exist inside the context + */ + public static Integer + getMemberReferenceIndex(String methodName, Integer offset) { + List<Integer> offsetList = + methodNameToMemberReferenceOffsets.get(methodName); + if (offsetList == null) { + return -1; + } + + return offsetList.indexOf(offset); + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/MethodCallScanner.java b/annotation-file-utilities/src/annotator/scanner/MethodCallScanner.java new file mode 100644 index 0000000..a104519 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/MethodCallScanner.java
@@ -0,0 +1,102 @@ +package annotator.scanner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +public class MethodCallScanner extends CommonScanner { + + /** + * Computes the index of the given method invocation amongst all + * method invocation trees inside its method, using 0-based indexing. + * + * @param origpath the path ending in the given method invocation tree + * @param tree the method invocation tree to search for + * @return the index of the given method invocation tree + */ + public static int indexOfMethodCallTree(TreePath origpath, Tree tree) { + TreePath path = findCountingContext(origpath); + if (path == null) { + return -1; + } + + MethodCallScanner mcs = new MethodCallScanner(tree); + mcs.scan(path, null); + return mcs.index; + } + + private int index; + private boolean done; + private final Tree tree; + + /** + * Creates an InstanceOfScanner that will scan the source tree for the + * given node representing the method invocation to find. + * @param tree the given method invocation to search for + */ + private MethodCallScanner(Tree tree) { + this.index = -1; + this.done = false; + this.tree = tree; + } + + @Override + public Void visitMethodInvocation(MethodInvocationTree node, Void p) { + if (!done) { + index++; + } + if (tree == node) { + done = true; + } + return super.visitMethodInvocation(node, p); + } + + // Map from name of a method to a list of bytecode offsets of all + // method invocations in that method. + private static Map<String, List<Integer>> methodNameToMethodCallOffsets = + new HashMap<String, List<Integer>>(); + + /** + * Adds a lambda expression bytecode offset to the current list of + * offsets for methodName. This method must be called with + * monotonically increasing offsets for any one method. + * + * @param methodName the name of the method + * @param offset the offset to add + */ + public static void addMethodCallToMethod(String methodName, Integer offset) { + List<Integer> offsetList = + methodNameToMethodCallOffsets.get(methodName); + if (offsetList == null) { + offsetList = new ArrayList<Integer>(); + methodNameToMethodCallOffsets.put(methodName, offsetList); + } + offsetList.add(offset); + } + + /** + * Returns the index of the given offset within the list of offsets + * for the given method, using 0-based indexing, or returns a negative + * number if the offset is not one of the offsets in the context. + * + * @param methodName the name of the method + * @param offset the offset of the lambda expression + * @return the index of the given offset, or a negative number + * if the offset does not exist inside the context + */ + public static Integer + getMethodCallIndex(String methodName, Integer offset) { + List<Integer> offsetList = + methodNameToMethodCallOffsets.get(methodName); + if (offsetList == null) { + return -1; + } + + return offsetList.indexOf(offset); + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/MethodOffsetClassVisitor.java b/annotation-file-utilities/src/annotator/scanner/MethodOffsetClassVisitor.java new file mode 100644 index 0000000..7834ca6 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/MethodOffsetClassVisitor.java
@@ -0,0 +1,219 @@ +package annotator.scanner; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodAdapter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.TypePath; + +import annotations.io.classfile.CodeOffsetAdapter; + +import com.sun.tools.javac.util.Pair; + + +/** + * MethodOffsetClassVisitor is a class visitor that should be passed to + * ASM's ClassReader in order to retrieve extra information about method + * offsets needed by all of the annotator.scanner classes. This visitor + * should visit every class that is to be annotated, and should be done + * before trying to match elements in the tree to the various criterion. + */ +// Note: in order to ensure all labels are visited, this class +// needs to extend ClassWriter and not other class visitor classes. +// There is no good reason why this is the case with ASM. +public class MethodOffsetClassVisitor extends ClassWriter { + CodeOffsetAdapter coa; + MethodVisitor mcoa; + + // This field should be set by entry on a method through visitMethod, + // and so all the visit* methods in LocalVariableMethodVisitor + private String methodName; + + public MethodOffsetClassVisitor(ClassReader cr) { + super(true, false); + this.methodName = "LocalVariableVisitor: DEFAULT_METHOD"; + coa = new CodeOffsetAdapter(cr); + } + + @Override + public MethodVisitor visitMethod(int access, String name, + String desc, String signature, String[ ] exceptions) { + methodName = name + desc.substring(0, desc.indexOf(")") + 1); + mcoa = coa.visitMethod(access, name, desc, signature, exceptions); + return new MethodOffsetMethodVisitor( + super.visitMethod(access, name, desc, signature, exceptions)); + } + + /** + * MethodOffsetMethodVisitor is the method visitor that + * MethodOffsetClassVisitor uses to visit particular methods and gather + * all the offset information by calling the appropriate static + * methods in annotator.scanner classes. + */ + private class MethodOffsetMethodVisitor extends MethodAdapter { + private Label lastLabel; + + public MethodOffsetMethodVisitor(MethodVisitor mv) { + super(mv); + lastLabel = null; + } + + private int labelOffset() { + try { + return lastLabel.getOffset(); + } catch (Exception ex) { + return 0; // TODO: find a better default? + } + } + + @Override + public void visitLocalVariable(String name, String desc, + String signature, Label start, Label end, int index) { + super.visitLocalVariable(name, desc, signature, start, end, index); + LocalVariableScanner.addToMethodNameIndexMap( + Pair.of(methodName, Pair.of(index, start.getOffset())), + name); + LocalVariableScanner.addToMethodNameCounter( + methodName, name, start.getOffset()); + mcoa.visitLocalVariable(name, desc, signature, start, end, index); + } + + @Override + public void visitLabel(Label label) { + super.visitLabel(label); + lastLabel = label; + mcoa.visitLabel(label); + } + + @Override + public void visitTypeInsn(int opcode, String desc) { + super.visitTypeInsn(opcode, desc); + switch (opcode) { + case Opcodes.CHECKCAST: + // CastScanner.addCastToMethod(methodName, labelOffset() + 1); + CastScanner.addCastToMethod(methodName, + coa.getMethodCodeOffset()); + break; + case Opcodes.NEW: + case Opcodes.ANEWARRAY: + NewScanner.addNewToMethod(methodName, labelOffset()); + break; + case Opcodes.INSTANCEOF: + InstanceOfScanner.addInstanceOfToMethod(methodName, + labelOffset() + 1); + break; + } + mcoa.visitTypeInsn(opcode, desc); + } + + @Override + public void visitMultiANewArrayInsn(String desc, int dims) { + super.visitMultiANewArrayInsn(desc, dims); + NewScanner.addNewToMethod(methodName, labelOffset()); + mcoa.visitMultiANewArrayInsn(desc, dims); + } + + @Override + public void visitIntInsn(int opcode, int operand) { + super.visitIntInsn(opcode, operand); + if (opcode == Opcodes.NEWARRAY) { + NewScanner.addNewToMethod(methodName, labelOffset()); + } + mcoa.visitIntInsn(opcode, operand); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, + String desc) { + super.visitMethodInsn(opcode, owner, name, desc); + switch (opcode) { + case Opcodes.INVOKEINTERFACE: + case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEVIRTUAL: + MethodCallScanner.addMethodCallToMethod(methodName, + labelOffset()); + break; + default: + break; + } + mcoa.visitMethodInsn(opcode, owner, name, desc); + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, + Handle bsm, Object... bsmArgs) { + super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + LambdaScanner.addLambdaExpressionToMethod(methodName, + labelOffset()); + mcoa.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + } + + @Override + public void visitCode() { + super.visitCode(); + mcoa.visitCode(); + } + + @Override + public void visitInsn(int opcode) { + super.visitInsn(opcode); + mcoa.visitInsn(opcode); + } + + @Override + public void visitVarInsn(int opcode, int var) { + super.visitVarInsn(opcode, var); + mcoa.visitVarInsn(opcode, var); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, + String desc) { + super.visitFieldInsn(opcode, owner, name, desc); + mcoa.visitFieldInsn(opcode, owner, name, desc); + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + super.visitJumpInsn(opcode, label); + mcoa.visitJumpInsn(opcode, label); + } + + @Override + public void visitLdcInsn(Object cst) { + super.visitLdcInsn(cst); + mcoa.visitLdcInsn(cst); + } + + @Override + public void visitIincInsn(int var, int increment) { + super.visitIincInsn(var, increment); + mcoa.visitIincInsn(var, increment); + } + + @Override + public void visitTableSwitchInsn(int min, int max, Label dflt, + Label[] labels) { + super.visitTableSwitchInsn(min, max, dflt, labels); + mcoa.visitTableSwitchInsn(min, max, dflt, labels); + } + + @Override + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + super.visitLookupSwitchInsn(dflt, keys, labels); + mcoa.visitLookupSwitchInsn(dflt, keys, labels); + } + + @Override + public void visitEnd() { + super.visitEnd(); + mcoa.visitEnd(); + } + } +}
diff --git a/annotation-file-utilities/src/annotator/scanner/NewScanner.java b/annotation-file-utilities/src/annotator/scanner/NewScanner.java new file mode 100644 index 0000000..5c18827 --- /dev/null +++ b/annotation-file-utilities/src/annotator/scanner/NewScanner.java
@@ -0,0 +1,123 @@ +package annotator.scanner; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.sun.source.tree.NewArrayTree; +import com.sun.source.tree.NewClassTree; +import com.sun.source.tree.Tree; +import com.sun.source.util.TreePath; + +import plume.Pair; + +/** + * NewScanner scans the source tree and determines the index of a given new, + * where the i^th index corresponds to the i^th new, using 0-based indexing. + */ +public class NewScanner extends CommonScanner { + private static boolean debug = false; + + static Map<Pair<TreePath,Tree>, Integer> cache = new HashMap<Pair<TreePath,Tree>, Integer>(); + + /** + * Computes the index of the given new tree amongst all new trees + * inside its method, using 0-based indexing. The tree has to be + * either a NewClassTree or a NewArrayTree. If the tree is not in + * a method, then the index is computed + * + * @param origpath + * the path ending in the given cast tree + * @param tree + * the cast tree to search for + * @return the index of the given cast tree + */ + public static int indexOfNewTree(TreePath origpath, Tree tree) { + debug("indexOfNewTree: " + origpath.getLeaf()); + + Pair<TreePath,Tree> args = Pair.of(origpath, tree); + if (cache.containsKey(args)) { + return cache.get(args); + } + + TreePath path = findCountingContext(origpath); + if (path == null) { + return -1; + } + + NewScanner lvts = new NewScanner(tree); + lvts.scan(path, null); + cache.put(args, lvts.index); + + return lvts.index; + } + + private int index = -1; + private boolean done = false; + private final Tree tree; + + private NewScanner(Tree tree) { + this.index = -1; + this.done = false; + this.tree = tree; + } + + @Override + public Void visitNewClass(NewClassTree node, Void p) { + if (!done) { + index++; + } + if (tree == node) { + done = true; + } + return super.visitNewClass(node, p); + } + + @Override + public Void visitNewArray(NewArrayTree node, Void p) { + if (!done) { + index++; + } + if (tree == node) { + done = true; + } + return super.visitNewArray(node, p); + } + + public static void debug(String s) { + if (debug) { + System.out.println(s); + } + } + + private static Map<String, List<Integer>> methodNameToNewOffsets = + new HashMap<String, List<Integer>>(); + + public static void addNewToMethod(String methodName, Integer offset) { + debug("adding new to method: " + methodName + " offset: " + offset); + List<Integer> offsetList = methodNameToNewOffsets.get(methodName); + if (offsetList == null) { + offsetList = new ArrayList<Integer>(); + methodNameToNewOffsets.put(methodName, offsetList); + } + offsetList.add(offset); + } + + public static Integer getMethodNewIndex(String methodName, Integer offset) { + List<Integer> offsetList = methodNameToNewOffsets.get(methodName); + if (offsetList == null) { + throw new RuntimeException("NewScanner.getMethodNewIndex() : " + + "did not find offsets for method: " + methodName); + } + + Integer offsetIndex = offsetList.indexOf(offset); + if (offsetIndex < 0) { + throw new RuntimeException("NewScanner.getMethodNewIndex() : " + + "in method: " + methodName + " did not find offset: " + + offset); + } + + return offsetIndex; + } +}
diff --git a/annotation-file-utilities/src/annotator/specification/CriterionList.java b/annotation-file-utilities/src/annotator/specification/CriterionList.java new file mode 100644 index 0000000..5580059 --- /dev/null +++ b/annotation-file-utilities/src/annotator/specification/CriterionList.java
@@ -0,0 +1,95 @@ +package annotator.specification; + +import annotator.find.Criteria; +import annotator.find.Criterion; + +// The notion of a CriterionList as a list of independent elements (with the +// list being satisfied if each of its elements is) is broken. For example, a +// class may have an inner class of the same name, and the list would be +// satisfied for either one. The same goes for method names, and (most +// problematically, because it comes up most often) for relative location in +// GenericArrayLocationCriterion, where the criterion [1] would also match [2 +// 1] because it is a suffix of it, or [2 1] would also match [3 2 1]. A +// related problem is the idea of checking from the bottom to the top of a +// TreePath whether it satisfies a criterion. I have put into place piecemeal +// fixes for some of these, but really the CriterionList needs to be turned +// into a path that is followed, and checking needs to start at the top of the +// tree (at the CompilationUnit). -MDE 9/2009 + +/** + * A CriterionList is a singly-linked list of Criterion meant to be treated + * as a stack. It is useful for creating base criteria and passing + * independent copies to different parts of a specification that creates + * all the criterion. A CriterionList is immutable, and so copies + * created by the add() function can safely be passed anywhere. + * It is supposed to be easier to manipulate than a Criteria. + */ +public class CriterionList { + // This really is a simple data structure to facilitate creation + // of specifications. TODO: make it a private class? + private Criterion current; + private CriterionList next; + + /** + * Creates a new CriterionList with no criterion. + */ + public CriterionList() { + next = null; + current = null; + } + + /** + * Creates a new CriterionList containing just the given Criterion. + * + * @param c the sole criterion the list contains at the moment + */ + public CriterionList(Criterion c) { + current = c; + next = null; + } + + private CriterionList(Criterion c, CriterionList n) { + current = c; + next = n; + } + + /** + * Adds the given criterion to the present list and returns a + * newly-allocated list containing the result. Does not modify its + * argument. + * + * @param c the criterion to add + * @return a new list containing the given criterion and the rest of the + * criterion already in this list + */ + public CriterionList add(Criterion c) { + return new CriterionList(c, this); + } + + /** + * Creates a Criteria object representing all the criterion in this list. + * + * @return a Criteria that contains all the criterion in this list + */ + public Criteria criteria() { + Criteria criteria = new Criteria(); + + CriterionList c = this; + while (c != null && c.current != null) { + criteria.add(c.current); + c = c.next; + } + + return criteria; + } + + @Override + public String toString() { + if (current == null) { return "[]"; } + StringBuilder sb = new StringBuilder("[").append(current); + for (CriterionList n = next; n.next != null ; n = n.next) { + sb.append(", ").append(n.current); + } + return sb.append("]").toString(); + } +}
diff --git a/annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java b/annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java new file mode 100644 index 0000000..79c1583 --- /dev/null +++ b/annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java
@@ -0,0 +1,744 @@ +package annotator.specification; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.objectweb.asm.ClassReader; + +import plume.FileIOException; +import plume.Pair; +import type.DeclaredType; +import type.Type; +import annotations.Annotation; +import annotations.el.ABlock; +import annotations.el.AClass; +import annotations.el.AElement; +import annotations.el.AExpression; +import annotations.el.AField; +import annotations.el.AMethod; +import annotations.el.AScene; +import annotations.el.ATypeElement; +import annotations.el.ATypeElementWithType; +import annotations.el.AnnotationDef; +import annotations.el.BoundLocation; +import annotations.el.InnerTypeLocation; +import annotations.el.LocalLocation; +import annotations.el.RelativeLocation; +import annotations.el.TypeIndexLocation; +import annotations.field.AnnotationFieldType; +import annotations.io.ASTPath; +import annotations.io.IndexFileParser; +import annotations.util.coll.VivifyingMap; +import annotator.find.AnnotationInsertion; +import annotator.find.CastInsertion; +import annotator.find.CloseParenthesisInsertion; +import annotator.find.ConstructorInsertion; +import annotator.find.Criteria; +import annotator.find.GenericArrayLocationCriterion; +import annotator.find.Insertion; +import annotator.find.IntersectionTypeLocationCriterion; +import annotator.find.NewInsertion; +import annotator.find.ReceiverInsertion; +import annotator.scanner.MethodOffsetClassVisitor; + +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; +import com.sun.source.tree.Tree; + +public class IndexFileSpecification implements Specification { + private final Multimap<Insertion, Annotation> insertionSources = + LinkedHashMultimap.<Insertion, Annotation>create(); + private final List<Insertion> insertions = new ArrayList<Insertion>(); + private final AScene scene; + private final String indexFileName; + + // If set, do not attempt to read class files with Asm. + // Mostly for debugging and workarounds. + public static boolean noAsm = false; + + private static boolean debug = false; + + private ConstructorInsertion cons = null; + + public IndexFileSpecification(String indexFileName) { + this.indexFileName = indexFileName; + scene = new AScene(); + } + + @Override + public List<Insertion> parse() throws FileIOException { + try { + Map<String, AnnotationDef> annotationDefs = + IndexFileParser.parseFile(indexFileName, scene); + Set<String> defKeys = annotationDefs.keySet(); + Set<String> ambiguous = new LinkedHashSet<String>(); + // If a qualified name's unqualified counterpart maps to null in + // defKeys, it means that the unqualified name is ambiguous and + // thus should always be qualified. + for (String key : defKeys) { + int ix = Math.max(key.lastIndexOf("."), key.lastIndexOf("$")); + if (ix >= 0) { + String name = key.substring(ix+1); + // containsKey() would give wrong result here + if (annotationDefs.get(name) == null) { ambiguous.add(name); } + } + } + Insertion.setAlwaysQualify(ambiguous); + } catch (FileIOException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException("Exception while parsing index file", e); + } + + if (debug) { + System.out.printf("Scene parsed from %s:%n", indexFileName); + System.out.println(scene.unparse()); + } + + parseScene(); +// debug("---------------------------------------------------------"); + return this.insertions; + } + + public Map<String, Set<String>> annotationImports() { + return scene.imports; + } + + public Multimap<Insertion, Annotation> insertionSources() { + return insertionSources; + } + + private void addInsertionSource(Insertion ins, Annotation anno) { + insertionSources.put(ins, anno); + } + + private static void debug(String s) { + if (debug) { + System.out.println(s); + } + } + + /* + private static void debug(String s, Object... args) { + if (debug) { + System.out.printf(s, args); + } + } + */ + + public AScene getScene() { return scene; } + + /** Fill in this.insertions with insertion pairs. */ + private void parseScene() { + debug("parseScene()"); + + // Empty criterion to work from. + CriterionList clist = new CriterionList(); + + VivifyingMap<String, AElement> packages = scene.packages; + for (Map.Entry<String, AElement> entry : packages.entrySet()) { + parsePackage(clist, entry.getKey(), entry.getValue()); + } + + VivifyingMap<String, AClass> classes = scene.classes; + for (Map.Entry<String, AClass> entry : classes.entrySet()) { + String key = entry.getKey(); + AClass clazz = entry.getValue(); + if (key.endsWith(".package-info")) { + // strip off suffix to get package name + parsePackage(clist, key.substring(0, key.length()-13), clazz); + } else { + parseClass(clist, key, clazz); + } + } + } + + // There is no .class file corresponding to the package-info.java file. + private void parsePackage(CriterionList clist, String packageName, AElement element) { + // There is no Tree.Kind.PACKAGE, only Tree.Kind.COMPILATION_UNIT. + // CompilationUnitTree has getPackageName and getPackageAnnotations + CriterionList packageClist = clist.add(Criteria.packageDecl(packageName)); + parseElement(packageClist, element); + } + + + + /** Fill in this.insertions with insertion pairs. + * @param className is fully qualified + */ + private void parseClass(CriterionList clist, String className, AClass clazz) { + cons = null; // 0 or 1 per class + if (! noAsm) { + // load extra info using asm + debug("parseClass(" + className + ")"); + try { + ClassReader classReader = new ClassReader(className); + MethodOffsetClassVisitor cv = new MethodOffsetClassVisitor(classReader); + classReader.accept(cv, false); + debug("Done reading " + className + ".class"); + } catch (IOException e) { + // If .class file not found, still proceed, in case + // user only wants method signature annotations. + // (TODO: It would be better to store which classes could not be + // found, then issue a warning only if an attempt is made to use + // the (missing) information. See + // https://github.com/typetools/annotation-tools/issues/34 .) + System.out.println("Warning: IndexFileSpecification did not find classfile for: " + className); + // throw new RuntimeException("IndexFileSpecification.parseClass: " + e); + } catch (RuntimeException e) { + System.err.println("IndexFileSpecification had a problem reading class: " + className); + throw e; + } catch (Error e) { + System.err.println("IndexFileSpecification had a problem reading class: " + className); + throw e; + } + } + + CriterionList clistSansClass = clist; + + clist = clist.add(Criteria.inClass(className, true)); + CriterionList classClist = clistSansClass.add(Criteria.is(Tree.Kind.CLASS, className)); + parseElement(classClist, clazz); + + VivifyingMap<BoundLocation, ATypeElement> bounds = clazz.bounds; + for (Entry<BoundLocation, ATypeElement> entry : bounds.entrySet()) { + BoundLocation boundLoc = entry.getKey(); + ATypeElement bound = entry.getValue(); + CriterionList boundList = clist.add(Criteria.classBound(className, boundLoc)); + for (Entry<InnerTypeLocation, ATypeElement> innerEntry : bound.innerTypes.entrySet()) { + InnerTypeLocation innerLoc = innerEntry.getKey(); + AElement ae = innerEntry.getValue(); + CriterionList innerBoundList = boundList.add(Criteria.atLocation(innerLoc)); + parseElement(innerBoundList, ae); + } + CriterionList outerClist = boundList.add(Criteria.atLocation()); + parseElement(outerClist, bound); + } + + clist = clist.add(Criteria.inClass(className, /*exactMatch=*/ false)); + + VivifyingMap<TypeIndexLocation, ATypeElement> extimpl = clazz.extendsImplements; + for (Entry<TypeIndexLocation, ATypeElement> entry : extimpl.entrySet()) { + TypeIndexLocation eiLoc = entry.getKey(); + ATypeElement ei = entry.getValue(); + CriterionList eiList = clist.add(Criteria.atExtImplsLocation(className, eiLoc)); + + for (Entry<InnerTypeLocation, ATypeElement> innerEntry : ei.innerTypes.entrySet()) { + InnerTypeLocation innerLoc = innerEntry.getKey(); + AElement ae = innerEntry.getValue(); + CriterionList innerBoundList = eiList.add(Criteria.atLocation(innerLoc)); + parseElement(innerBoundList, ae); + } + parseElement(eiList, ei); + } + + parseASTInsertions(clist, clazz.insertAnnotations, clazz.insertTypecasts); + + for (Map.Entry<String, AField> entry : clazz.fields.entrySet()) { +// clist = clist.add(Criteria.notInMethod()); // TODO: necessary? what is in class but not in method? + parseField(clist, entry.getKey(), entry.getValue()); + } + for (Map.Entry<String, AMethod> entry : clazz.methods.entrySet()) { + parseMethod(clist, className, entry.getKey(), entry.getValue()); + } + for (Map.Entry<Integer, ABlock> entry : clazz.staticInits.entrySet()) { + parseStaticInit(clist, className, entry.getKey(), entry.getValue()); + } + for (Map.Entry<Integer, ABlock> entry : clazz.instanceInits.entrySet()) { + parseInstanceInit(clist, className, entry.getKey(), entry.getValue()); + } + for (Map.Entry<String, AExpression> entry : clazz.fieldInits.entrySet()) { + parseFieldInit(clist, className, entry.getKey(), entry.getValue()); + } + + debug("parseClass(" + className + "): done"); + } + + /** Fill in this.insertions with insertion pairs. */ + private void parseField(CriterionList clist, String fieldName, AField field) { + // parse declaration annotations + parseElement(clist.add(Criteria.field(fieldName, true)), field); + + // parse type annotations + clist = clist.add(Criteria.field(fieldName, false)); + parseInnerAndOuterElements(clist, field.type); + parseASTInsertions(clist, field.insertAnnotations, field.insertTypecasts); + } + + private void parseStaticInit(CriterionList clist, String className, int blockID, ABlock block) { + clist = clist.add(Criteria.inStaticInit(blockID)); + // the method name argument is not used for static initializers, which are only used + // in source specifications. Same for instance and field initializers. + // the empty () are there to prevent the whole string to be removed in later parsing. + parseBlock(clist, className, "static init number " + blockID + "()", block); + } + + private void parseInstanceInit(CriterionList clist, String className, int blockID, ABlock block) { + clist = clist.add(Criteria.inInstanceInit(blockID)); + parseBlock(clist, className, "instance init number " + blockID + "()", block); + } + + // keep the descriptive strings for field initializers and static inits consistent + // with text used in NewCriterion. + + private void parseFieldInit(CriterionList clist, String className, String fieldName, AExpression exp) { + clist = clist.add(Criteria.inFieldInit(fieldName)); + parseExpression(clist, className, "init for field " + fieldName + "()", exp); + } + + /** + * Fill in this.insertions with insertion pairs. + * @param clist the criteria specifying the location of the insertions + * @param element holds the annotations to be inserted + * @return a list of the {@link AnnotationInsertion}s that are created + */ + private List<Insertion> parseElement(CriterionList clist, AElement element) { + return parseElement(clist, element, new ArrayList<Insertion>(), false); + } + + /** + * Fill in this.insertions with insertion pairs. + * @param clist the criteria specifying the location of the insertions + * @param element holds the annotations to be inserted + * @param isCastInsertion {@code true} if this for a cast insertion, {@code false} + * otherwise. + * @return a list of the {@link AnnotationInsertion}s that are created + */ + private List<Insertion> parseElement(CriterionList clist, AElement element, + boolean isCastInsertion) { + return parseElement(clist, element, new ArrayList<Insertion>(), isCastInsertion); + } + + /** + * Fill in this.insertions with insertion pairs. + * @param clist the criteria specifying the location of the insertions + * @param element holds the annotations to be inserted + * @param add {@code true} if the create {@link AnnotationInsertion}s should + * be added to {@link #insertions}, {@code false} otherwise. + * @return a list of the {@link AnnotationInsertion}s that are created + */ + private List<Insertion> parseElement(CriterionList clist, AElement element, + List<Insertion> innerTypeInsertions) { + return parseElement(clist, element, innerTypeInsertions, false); + } + + /** + * Fill in this.insertions with insertion pairs. + * @param clist the criteria specifying the location of the insertions + * @param element holds the annotations to be inserted + * @param innerTypeInsertions the insertions on the inner type of this + * element. This is only used for receiver and "new" insertions. + * See {@link ReceiverInsertion} for more details. + * @param isCastInsertion {@code true} if this for a cast insertion, {@code false} + * otherwise. + * @return a list of the {@link AnnotationInsertion}s that are created + */ + private List<Insertion> parseElement(CriterionList clist, AElement element, + List<Insertion> innerTypeInsertions, boolean isCastInsertion) { + // Use at most one receiver and one cast insertion and add all of the + // annotations to the one insertion. + ReceiverInsertion receiver = null; + NewInsertion neu = null; + CastInsertion cast = null; + CloseParenthesisInsertion closeParen = null; + List<Insertion> annotationInsertions = new ArrayList<Insertion>(); + Set<Pair<String, Annotation>> elementAnnotations = getElementAnnotations(element); + if (elementAnnotations.isEmpty()) { + Criteria criteria = clist.criteria(); + if (element instanceof ATypeElementWithType) { + // Still insert even if it's a cast insertion with no outer + // annotations to just insert a cast, or insert a cast with + // annotations on the compound types. + Pair<CastInsertion, CloseParenthesisInsertion> pair = + createCastInsertion(((ATypeElementWithType) element).getType(), + null, innerTypeInsertions, criteria); + cast = pair.a; + closeParen = pair.b; + } else if (!innerTypeInsertions.isEmpty()) { + if (isOnReceiver(criteria)) { + receiver = new ReceiverInsertion(new DeclaredType(), + criteria, innerTypeInsertions); + } else if (isOnNew(criteria)) { + neu = new NewInsertion(new DeclaredType(), + criteria, innerTypeInsertions); + } + } + } + + for (Pair<String, Annotation> p : elementAnnotations) { + List<Insertion> elementInsertions = new ArrayList<Insertion>(); + String annotationString = p.a; + Annotation annotation = p.b; + Criteria criteria = clist.criteria(); + Boolean isDeclarationAnnotation = !annotation.def.isTypeAnnotation() + || criteria.isOnFieldDeclaration(); + if (noTypePath(criteria) && isOnReceiver(criteria)) { + if (receiver == null) { + DeclaredType type = new DeclaredType(); + type.addAnnotation(annotationString); + receiver = new ReceiverInsertion(type, criteria, innerTypeInsertions); + elementInsertions.add(receiver); + } else { + receiver.getType().addAnnotation(annotationString); + } + addInsertionSource(receiver, annotation); + } else if (noTypePath(criteria) && isOnNew(criteria)) { + if (neu == null) { + DeclaredType type = new DeclaredType(); + type.addAnnotation(annotationString); + neu = new NewInsertion(type, criteria, innerTypeInsertions); + elementInsertions.add(neu); + } else { + neu.getType().addAnnotation(annotationString); + } + addInsertionSource(neu, annotation); + } else if (element instanceof ATypeElementWithType) { + if (cast == null) { + Pair<CastInsertion, CloseParenthesisInsertion> insertions = createCastInsertion( + ((ATypeElementWithType) element).getType(), annotationString, + innerTypeInsertions, criteria); + cast = insertions.a; + closeParen = insertions.b; + elementInsertions.add(cast); + elementInsertions.add(closeParen); + // no addInsertionSource, as closeParen is not explicit in scene + } else { + cast.getType().addAnnotation(annotationString); + } + addInsertionSource(cast, annotation); + } else { + RelativeLocation loc = criteria.getCastRelativeLocation(); + if (loc != null && loc.type_index > 0) { + criteria.add(new IntersectionTypeLocationCriterion(loc)); + } + Insertion ins = new AnnotationInsertion(annotationString, criteria, + isDeclarationAnnotation); + debug("parsed: " + ins); + if (!isCastInsertion) { + // Annotations on compound types of a cast insertion will be + // inserted directly on the cast insertion. + // this.insertions.add(ins); + elementInsertions.add(ins); + } + annotationInsertions.add(ins); + addInsertionSource(ins, annotation); + } + this.insertions.addAll(elementInsertions); + + // exclude expression annotations + if (noTypePath(criteria) && isOnNullaryConstructor(criteria)) { + if (cons == null) { + DeclaredType type = new DeclaredType(criteria.getClassName()); + cons = new ConstructorInsertion(type, criteria, + new ArrayList<Insertion>()); + this.insertions.add(cons); + } + // no addInsertionSource, as cons is not explicit in scene + for (Insertion i : elementInsertions) { + if (i.getKind() == Insertion.Kind.RECEIVER) { + cons.addReceiverInsertion((ReceiverInsertion) i); + } else if (criteria.isOnReturnType()) { + ((DeclaredType) cons.getType()).addAnnotation(annotationString); + } else if (isDeclarationAnnotation) { + cons.addDeclarationInsertion(i); + i.setInserted(true); + } else { + annotationInsertions.add(i); + } + } + } + elementInsertions.clear(); + } + if (receiver != null) { + this.insertions.add(receiver); + } + if (neu != null) { + this.insertions.add(neu); + } + if (cast != null) { + this.insertions.add(cast); + this.insertions.add(closeParen); + } + return annotationInsertions; + } + + private boolean noTypePath(Criteria criteria) { + GenericArrayLocationCriterion galc = + criteria.getGenericArrayLocation(); + return galc == null || galc.getLocation().isEmpty(); + } + + public static boolean isOnReceiver(Criteria criteria) { + ASTPath astPath = criteria.getASTPath(); + if (astPath == null) { return criteria.isOnReceiver(); } + if (astPath.isEmpty()) { return false; } + ASTPath.ASTEntry entry = astPath.get(-1); + return entry.childSelectorIs(ASTPath.PARAMETER) + && entry.getArgument() < 0; + } + + public static boolean isOnNew(Criteria criteria) { + ASTPath astPath = criteria.getASTPath(); + if (astPath == null || astPath.isEmpty()) { return criteria.isOnNew(); } + ASTPath.ASTEntry entry = astPath.get(-1); + Tree.Kind kind = entry.getTreeKind(); + return kind == Tree.Kind.NEW_ARRAY + && entry.childSelectorIs(ASTPath.TYPE) + && entry.getArgument() == 0 + || kind == Tree.Kind.NEW_CLASS + && entry.childSelectorIs(ASTPath.IDENTIFIER); + } + + private static boolean isOnNullaryConstructor(Criteria criteria) { + if (criteria.isOnMethod("<init>()V")) { + ASTPath astPath = criteria.getASTPath(); + if (astPath == null || astPath.isEmpty()) { + return !criteria.isOnNew(); // exclude expression annotations + } + ASTPath.ASTEntry entry = astPath.get(0); + return entry.getTreeKind() == Tree.Kind.METHOD + && (entry.childSelectorIs(ASTPath.TYPE) || isOnReceiver(criteria)); + } + return false; + } + + /** + * Creates the {@link CastInsertion} and {@link CloseParenthesisInsertion} + * for a cast insertion. + * + * @param type the cast type to insert + * @param annotationString the annotation on the outermost type, or + * {@code null} if none. With no outermost annotation this cast + * insertion will either be a cast without any annotations or a cast + * with annotations only on the compound types. + * @param innerTypeInsertions the annotations on the inner types + * @param criteria the criteria for the location of this insertion + * @return the {@link CastInsertion} and {@link CloseParenthesisInsertion} + */ + private Pair<CastInsertion, CloseParenthesisInsertion> createCastInsertion( + Type type, String annotationString, List<Insertion> innerTypeInsertions, + Criteria criteria) { + if (annotationString != null) { + type.addAnnotation(annotationString); + } + Insertion.decorateType(innerTypeInsertions, type, criteria.getASTPath()); + CastInsertion cast = new CastInsertion(criteria, type); + CloseParenthesisInsertion closeParen = new CloseParenthesisInsertion( + criteria, cast.getSeparateLine()); + return new Pair<CastInsertion, CloseParenthesisInsertion>(cast, closeParen); + } + + /** + * Fill in this.insertions with insertion pairs for the outer and inner types. + * @param clist the criteria specifying the location of the insertions + * @param typeElement holds the annotations to be inserted + */ + private void parseInnerAndOuterElements(CriterionList clist, ATypeElement typeElement) { + parseInnerAndOuterElements(clist, typeElement, false); + } + + /** + * Fill in this.insertions with insertion pairs for the outer and inner types. + * @param clist the criteria specifying the location of the insertions + * @param typeElement holds the annotations to be inserted + * @param isCastInsertion {@code true} if this for a cast insertion, {@code false} + * otherwise. + */ + private void parseInnerAndOuterElements(CriterionList clist, + ATypeElement typeElement, boolean isCastInsertion) { + List<Insertion> innerInsertions = new ArrayList<Insertion>(); + for (Entry<InnerTypeLocation, ATypeElement> innerEntry: typeElement.innerTypes.entrySet()) { + InnerTypeLocation innerLoc = innerEntry.getKey(); + AElement innerElement = innerEntry.getValue(); + CriterionList innerClist = clist.add(Criteria.atLocation(innerLoc)); + innerInsertions.addAll(parseElement(innerClist, innerElement, isCastInsertion)); + } + CriterionList outerClist = clist; + if (!isCastInsertion) { + // Cast insertion is never on an existing type. + outerClist = clist.add(Criteria.atLocation()); + } + parseElement(outerClist, typeElement, innerInsertions); + } + + // Returns a string representation of the annotations at the element. + private Set<Pair<String, Annotation>> + getElementAnnotations(AElement element) { + Set<Pair<String, Annotation>> result = + new LinkedHashSet<Pair<String, Annotation>>( + element.tlAnnotationsHere.size()); + for (Annotation a : element.tlAnnotationsHere) { + AnnotationDef adef = a.def; + String annotationString = "@" + adef.name; + + if (a.fieldValues.size() == 1 && a.fieldValues.containsKey("value")) { + annotationString += "(" + formatFieldValue(a, "value") + ")"; + } else if (a.fieldValues.size() > 0) { + annotationString += "("; + boolean first = true; + for (String field : a.fieldValues.keySet()) { + // parameters of the annotation + if (!first) { + annotationString += ", "; + } + annotationString += field + "=" + formatFieldValue(a, field); + first = false; + } + annotationString += ")"; + } + // annotationString += " "; + result.add(new Pair<String, Annotation>(annotationString, a)); + } + return result; + } + + private String formatFieldValue(Annotation a, String field) { + AnnotationFieldType fieldType = a.def.fieldTypes.get(field); + assert fieldType != null; + return fieldType.format(a.fieldValues.get(field)); + } + + private void parseMethod(CriterionList clist, String className, String methodName, AMethod method) { + // Being "in" a method refers to being somewhere in the + // method's tree, which includes return types, parameters, receiver, and + // elements inside the method body. + clist = clist.add(Criteria.inMethod(methodName)); + + // parse declaration annotations + parseElement(clist, method); + + // parse receiver + CriterionList receiverClist = clist.add(Criteria.receiver(methodName)); + parseInnerAndOuterElements(receiverClist, method.receiver.type); + + // parse return type + CriterionList returnClist = clist.add(Criteria.returnType(className, methodName)); + parseInnerAndOuterElements(returnClist, method.returnType); + + // parse bounds of method + for (Entry<BoundLocation, ATypeElement> entry : method.bounds.entrySet()) { + BoundLocation boundLoc = entry.getKey(); + ATypeElement bound = entry.getValue(); + CriterionList boundClist = clist.add(Criteria.methodBound(methodName, boundLoc)); + parseInnerAndOuterElements(boundClist, bound); + } + + // parse parameters of method + for (Entry<Integer, AField> entry : method.parameters.entrySet()) { + Integer index = entry.getKey(); + AField param = entry.getValue(); + CriterionList paramClist = clist.add(Criteria.param(methodName, index)); + // parse declaration annotations + // parseField(paramClist, index.toString(), param); + parseInnerAndOuterElements(paramClist, param.type); + } + + // parse insert annotations/typecasts of method + parseASTInsertions(clist, method.insertAnnotations, method.insertTypecasts); + + // parse body of method + parseBlock(clist, className, methodName, method.body); + } + + private void parseBlock(CriterionList clist, String className, String methodName, ABlock block) { + // parse locals of method + for (Entry<LocalLocation, AField> entry : block.locals.entrySet()) { + LocalLocation loc = entry.getKey(); + AElement var = entry.getValue(); + CriterionList varClist = clist.add(Criteria.local(methodName, loc)); + // parse declaration annotations + parseElement(varClist, var); + parseInnerAndOuterElements(varClist, var.type); + } + + parseExpression(clist, className, methodName, block); + } + + private void parseExpression(CriterionList clist, String className, String methodName, AExpression exp) { + // parse typecasts of method + for (Entry<RelativeLocation, ATypeElement> entry : exp.typecasts.entrySet()) { + RelativeLocation loc = entry.getKey(); + ATypeElement cast = entry.getValue(); + CriterionList castClist = clist.add(Criteria.cast(methodName, loc)); + parseInnerAndOuterElements(castClist, cast); + } + + // parse news (object creation) of method + for (Entry<RelativeLocation, ATypeElement> entry : exp.news.entrySet()) { + RelativeLocation loc = entry.getKey(); + ATypeElement newObject = entry.getValue(); + CriterionList newClist = clist.add(Criteria.newObject(methodName, loc)); + parseInnerAndOuterElements(newClist, newObject); + } + + // parse instanceofs of method + for (Entry<RelativeLocation, ATypeElement> entry : exp.instanceofs.entrySet()) { + RelativeLocation loc = entry.getKey(); + ATypeElement instanceOf = entry.getValue(); + CriterionList instanceOfClist = clist.add(Criteria.instanceOf(methodName, loc)); + parseInnerAndOuterElements(instanceOfClist, instanceOf); + } + + // parse member references of method + for (Entry<RelativeLocation, ATypeElement> entry : exp.refs.entrySet()) { + RelativeLocation loc = entry.getKey(); + ATypeElement ref = entry.getValue(); + CriterionList instanceOfClist = + clist.add(Criteria.memberReference(methodName, loc)); + parseInnerAndOuterElements(instanceOfClist, ref); + } + + // parse method invocations of method + for (Entry<RelativeLocation, ATypeElement> entry : exp.calls.entrySet()) { + RelativeLocation loc = entry.getKey(); + ATypeElement call = entry.getValue(); + CriterionList instanceOfClist = + clist.add(Criteria.methodCall(methodName, loc)); + parseInnerAndOuterElements(instanceOfClist, call); + } + + // parse lambda expressions of method + for (Entry<RelativeLocation, AMethod> entry : exp.funs.entrySet()) { + RelativeLocation loc = entry.getKey(); + AMethod lambda = entry.getValue(); + CriterionList lambdaClist = clist.add(Criteria.lambda(methodName, loc)); + parseLambdaExpression(className, methodName, lambda, lambdaClist); + } + } + + private void parseLambdaExpression(String className, String methodName, + AMethod lambda, CriterionList clist) { + for (Entry<Integer, AField> entry : lambda.parameters.entrySet()) { + Integer index = entry.getKey(); + AField param = entry.getValue(); + CriterionList paramClist = clist.add(Criteria.param("(anonymous)", index)); + parseInnerAndOuterElements(paramClist, param.type); + parseASTInsertions(paramClist, param.insertAnnotations, param.insertTypecasts); + } + parseBlock(clist, className, methodName, lambda.body); + } + + private void parseASTInsertions(CriterionList clist, + VivifyingMap<ASTPath, ATypeElement> insertAnnotations, + VivifyingMap<ASTPath, ATypeElementWithType> insertTypecasts) { + for (Entry<ASTPath, ATypeElement> entry : insertAnnotations.entrySet()) { + ASTPath astPath = entry.getKey(); + ATypeElement insertAnnotation = entry.getValue(); + CriterionList insertAnnotationClist = + clist.add(Criteria.astPath(astPath)); + parseInnerAndOuterElements(insertAnnotationClist, + insertAnnotation, true); + } + for (Entry<ASTPath, ATypeElementWithType> entry : insertTypecasts.entrySet()) { + ASTPath astPath = entry.getKey(); + ATypeElementWithType insertTypecast = entry.getValue(); + CriterionList insertTypecastClist = clist.add(Criteria.astPath(astPath)); + parseInnerAndOuterElements(insertTypecastClist, insertTypecast, true); + } + } +}
diff --git a/annotation-file-utilities/src/annotator/specification/Specification.java b/annotation-file-utilities/src/annotator/specification/Specification.java new file mode 100644 index 0000000..6477e47 --- /dev/null +++ b/annotation-file-utilities/src/annotator/specification/Specification.java
@@ -0,0 +1,22 @@ +package annotator.specification; + +import java.util.List; + +import plume.FileIOException; +import annotator.find.Insertion; +// import annotations.io.FileIOException; + +/** + * Represents a file containing a "specification" for placing annotations on + * program elements. + */ +public interface Specification { + + /** + * Parses the specification file. + * + * @return the insertions that the annotator should make, as determined from + * parsing the specification file + */ + List<Insertion> parse() throws FileIOException; +}
diff --git a/annotation-file-utilities/tests/-SEE-THE-MAKEFILE- b/annotation-file-utilities/tests/-SEE-THE-MAKEFILE- new file mode 100644 index 0000000..f11c322 --- /dev/null +++ b/annotation-file-utilities/tests/-SEE-THE-MAKEFILE-
@@ -0,0 +1 @@ +See the Makefile.
diff --git a/annotation-file-utilities/tests/ASTInsert.goal b/annotation-file-utilities/tests/ASTInsert.goal new file mode 100644 index 0000000..73327d4 --- /dev/null +++ b/annotation-file-utilities/tests/ASTInsert.goal
@@ -0,0 +1,99 @@ +import java.io.PrintStream; +import java.util.List; +import java.util.Map; + +@interface Bla {} + +public class ASTInsert { + + PrintStream out; + private int c = ((@checkers.nullness.quals.Nullable int) (((@checkers.nullness.quals.Nullable int) (12)) + ((@checkers.nullness.quals.Nullable int) (13)))); + private String str = "this" + ((@checkers.nullness.quals.Nullable String) ("is")).concat(((@checkers.nullness.quals.Nullable String) ("string"))); + private @Bla String @Bla [] sa = new @Bla String @Bla [] {}; + + void m() { + int i; + } + + int m(String y, String[] z, int i) { + String x = ((@checkers.nullness.quals.Nullable String) (new String())); + String s; + s = ((@checkers.nullness.quals.Nullable String) (((@checkers.nullness.quals.Nullable String) (x)) + ((@checkers.nullness.quals.Nullable String) (x)))); + s = y; + s = z[((@checkers.nullness.quals.Nullable int) (0))]; + s = x; + int j = 0; + switch (((@checkers.nullness.quals.Nullable int) (i + 2))) { + case 1: + j = i + ((@checkers.nullness.quals.Nullable int) (1)); + System.out.println(1); + break; + case 2: + j = i + 2; + System.out.println(((@checkers.nullness.quals.Nullable int) (2))); + break; + default: + j = i + 3; + System.out.println(-1); + } + j *= ((@checkers.nullness.quals.Nullable int) (i)); + j = s != x ? ((@checkers.nullness.quals.Nullable int) (j)) : i; + do { + int h = ((@checkers.nullness.quals.Nullable int) (i)) & j; + } while (i < j); + for (int i2 : new int[5]) { + j = ((@checkers.nullness.quals.Nullable int) (i2)); + } + for (int a = 0, b = ((@checkers.nullness.quals.Nullable int) (0)); a < ((@checkers.nullness.quals.Nullable int) (j)); a = ((@checkers.nullness.quals.Nullable int) (a + 1)), b++) + a = ((@checkers.nullness.quals.Nullable int) (b)); + if (((@checkers.nullness.quals.Nullable int) (i)) < j) + i = j; + else + j = ((@checkers.nullness.quals.Nullable int) (i)); + boolean b = ((@checkers.nullness.quals.Nullable String) (x)) instanceof String; + label: b = ((@checkers.nullness.quals.Nullable boolean) (false)); + Object o = ((@checkers.nullness.quals.Nullable ASTInsert) (this)).out; + m(y, z, ((@checkers.nullness.quals.Nullable int) (i))); + int[][] test = new int[4][((@checkers.nullness.quals.Nullable int) (5))]; + int[][] test2 = {{1, 2}, {1, 2, ((@checkers.nullness.quals.Nullable int) (3))}}; + new String(((@checkers.nullness.quals.Nullable String) ("test"))); + if (i < 1) + return ((@checkers.nullness.quals.Nullable int) (18)); + synchronized (o) { + i = ((@checkers.nullness.quals.Nullable int) (i + i)); + } + if (j < 1) + throw ((@checkers.nullness.quals.Nullable IllegalStateException) (new IllegalStateException())); + try { + int t = ((@checkers.nullness.quals.Nullable int) (1)); + } catch (Error e) { + i = j; + } catch (RuntimeException e) { + j = ((@checkers.nullness.quals.Nullable int) (i)); + } finally { + j = i + ((@checkers.nullness.quals.Nullable int) (j)); + } + j = (int) (i + ((@checkers.nullness.quals.Nullable int) (j))); + j = - ((@checkers.nullness.quals.Nullable int) (j)); + while (i < ((@checkers.nullness.quals.Nullable int) (j))) + i = ((@checkers.nullness.quals.Nullable int) (i)) + 1; + ((@checkers.nullness.quals.Nullable ASTInsert) (this)).out.println(); + ((@checkers.nullness.quals.Nullable PrintStream) (System.out)).println(); + Object obj = ((@checkers.nullness.quals.Nullable @A @B @C Object) (null)); + return 0; + } + + public <T> void invoc(T t1, T t2) {} + public void context() { + this.<@checkers.nullness.quals.NonNull String>invoc("a", null); + } +} + +class Wild<X extends List<? extends @Bla Object>> { + Wild(@Bla Wild<X> n, X p) { + } +} + +class Unbound<X extends @A Object> {} + +class Bound<@A X extends @B Object & @C Comparable<@D int @E []> & @F Map<@G ? extends @H Object, @I ?>, @J Y> {}
diff --git a/annotation-file-utilities/tests/ASTInsert.jaif b/annotation-file-utilities/tests/ASTInsert.jaif new file mode 100644 index 0000000..fa88b47 --- /dev/null +++ b/annotation-file-utilities/tests/ASTInsert.jaif
@@ -0,0 +1,101 @@ +package: +annotation @Bla: +annotation @A: +annotation @B: +annotation @C: +annotation @D: +annotation @E: +annotation @F: +annotation @G: +annotation @H: +annotation @I: +annotation @J: + +package checkers.nullness.quals: +annotation @Nullable: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) +annotation @NonNull: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) + +package : +class ASTInsert: + + field c: + insert-annotation Variable.initializer, Binary.rightOperand: @Nullable + insert-annotation Variable.initializer, Binary.leftOperand: @Nullable + insert-annotation Variable.initializer: @Nullable + + field str: + insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable + insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @Nullable + + field sa: + insert-annotation Variable.initializer, NewArray.type 0: @Bla + insert-annotation Variable.type, ArrayType.type: @Bla + insert-annotation Variable.type: @Bla + insert-annotation Variable.initializer, NewArray.type 1: @Bla + + method m(Ljava/lang/String;[Ljava/lang/String;I)I: + insert-annotation Block.statement 0, Variable.initializer: @Nullable + insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable + insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable + insert-annotation Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @Nullable + insert-annotation Block.statement 7, Switch.expression, Parenthesized.expression: @Nullable + insert-annotation Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable + insert-annotation Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @Nullable + insert-annotation Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @Nullable + insert-annotation Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @Nullable + insert-annotation Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @Nullable + insert-annotation Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 12, ForLoop.initializer 1, Variable.initializer: @Nullable + insert-annotation Block.statement 12, ForLoop.condition, Binary.rightOperand: @Nullable + insert-annotation Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @Nullable + insert-annotation Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 14, Variable.initializer, InstanceOf.expression: @Nullable + insert-annotation Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 16, Variable.initializer, MemberSelect.expression: @Nullable + insert-annotation Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @Nullable + insert-annotation Block.statement 18, Variable.initializer, NewArray.dimension 1: @Nullable + insert-annotation Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @Nullable + insert-annotation Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @Nullable + insert-annotation Block.statement 21, If.thenStatement, Return.expression: @Nullable + insert-annotation Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 23, If.thenStatement, Throw.expression: @Nullable + insert-annotation Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @Nullable + insert-annotation Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable + insert-annotation Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable + insert-annotation Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @Nullable + insert-annotation Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @Nullable + insert-annotation Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @Nullable + insert-annotation Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable + insert-annotation Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @Nullable + insert-annotation Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable + insert-annotation Block.statement 30, Variable.initializer: @Nullable @A @B @C + + method context()V: + insert-annotation Block.statement 0, ExpressionStatement.expression, MethodInvocation.typeArgument 0: @NonNull + +class Wild: +bound 0 & 0: +inner-type 3, 0, 2, 0: @Bla + +class Wild: +method <init>(LWild;Ljava/util/List;)V: +parameter 0: +type: @Bla + +class Unbound: +bound 0 & 0: @A + +class Bound: + typeparam 0: @A + typeparam 1: @J + bound 0 & 0: @B + bound 0 & 1: @C + inner-type 3, 0: @E + inner-type 3, 0, 0, 0: @D + bound 0 & 2: @F + inner-type 3, 0: @G + inner-type 3, 0, 2, 0: @H + inner-type 3, 1: @I
diff --git a/annotation-file-utilities/tests/ASTInsert.java b/annotation-file-utilities/tests/ASTInsert.java new file mode 100644 index 0000000..b98916f --- /dev/null +++ b/annotation-file-utilities/tests/ASTInsert.java
@@ -0,0 +1,99 @@ +import java.io.PrintStream; +import java.util.List; +import java.util.Map; + +@interface Bla {} + +public class ASTInsert { + + PrintStream out; + private int c = 12 + 13; + private String str = "this" + "is".concat("string"); + private String[] sa = {}; + + void m() { + int i; + } + + int m(String y, String[] z, int i) { + String x = new String(); + String s; + s = x + x; + s = y; + s = z[0]; + s = x; + int j = 0; + switch (i + 2) { + case 1: + j = i + 1; + System.out.println(1); + break; + case 2: + j = i + 2; + System.out.println(2); + break; + default: + j = i + 3; + System.out.println(-1); + } + j *= i; + j = s != x ? j : i; + do { + int h = i & j; + } while (i < j); + for (int i2 : new int[5]) { + j = i2; + } + for (int a = 0, b = 0; a < j; a = a + 1, b++) + a = b; + if (i < j) + i = j; + else + j = i; + boolean b = x instanceof String; + label: b = false; + Object o = this.out; + m(y, z, i); + int[][] test = new int[4][5]; + int[][] test2 = {{1, 2}, {1, 2, 3}}; + new String("test"); + if (i < 1) + return 18; + synchronized (o) { + i = i + i; + } + if (j < 1) + throw new IllegalStateException(); + try { + int t = 1; + } catch (Error e) { + i = j; + } catch (RuntimeException e) { + j = i; + } finally { + j = i + j; + } + j = (int) (i + j); + j = -j; + while (i < j) + i = i + 1; + this.out.println(); + System.out.println(); + Object obj = null; + return 0; + } + + public <T> void invoc(T t1, T t2) {} + public void context() { + this.<String>invoc("a", null); + } +} + +class Wild<X extends List<?>> { + Wild(Wild<X> n, X p) { + } +} + +class Unbound<X> {} + +class Bound<X extends Object & Comparable<int[]> & Map<? extends Object, ?>, Y> {}
diff --git a/annotation-file-utilities/tests/Abbreviation.goal b/annotation-file-utilities/tests/Abbreviation.goal new file mode 100644 index 0000000..ee67d1b --- /dev/null +++ b/annotation-file-utilities/tests/Abbreviation.goal
@@ -0,0 +1,8 @@ +// Try to add annotations from different packages that have the same +// name. Results should be the same whether _abbreviate_ is set to true +// or to false. +class Abbreviation { + public Abbreviation(@org.checkerframework.checker.tainting.qual.Tainted Object o) { + } +} +
diff --git a/annotation-file-utilities/tests/Abbreviation.jaif b/annotation-file-utilities/tests/Abbreviation.jaif new file mode 100644 index 0000000..8c94f85 --- /dev/null +++ b/annotation-file-utilities/tests/Abbreviation.jaif
@@ -0,0 +1,9 @@ +package org.checkerframework.checker.tainting.qual: +annotation @Tainted: + +package : +class Abbreviation: + method <init>(Ljava/lang/Object;)V: + parameter 0: + type: @org.checkerframework.checker.tainting.qual.Tainted +
diff --git a/annotation-file-utilities/tests/Abbreviation.java b/annotation-file-utilities/tests/Abbreviation.java new file mode 100644 index 0000000..6f4a893 --- /dev/null +++ b/annotation-file-utilities/tests/Abbreviation.java
@@ -0,0 +1,8 @@ +// Try to add annotations from different packages that have the same +// name. Results should be the same whether _abbreviate_ is set to true +// or to false. +class Abbreviation { + public Abbreviation(Object o) { + } +} +
diff --git a/annotation-file-utilities/tests/AnonInner.goal b/annotation-file-utilities/tests/AnonInner.goal new file mode 100644 index 0000000..81ea573 --- /dev/null +++ b/annotation-file-utilities/tests/AnonInner.goal
@@ -0,0 +1,19 @@ +public class AnonInner { + class InnerOne { + Object m1(String p) { + return new @A Object() { + public @B Object e1(Object o) { + return new @C Object(); + } + }; + } + Object m2(String p) { + return new @D Object() { + public @E Object e2(Object o) { + return new @F Object(); + } + }; + } + + } +}
diff --git a/annotation-file-utilities/tests/AnonInner.jaif b/annotation-file-utilities/tests/AnonInner.jaif new file mode 100644 index 0000000..6fa99c4 --- /dev/null +++ b/annotation-file-utilities/tests/AnonInner.jaif
@@ -0,0 +1,43 @@ +package: +annotation @A: +package: +annotation @B: +package: +annotation @C: +package: +annotation @D: +package: +annotation @E: +package: +annotation @F: + + +package: +class AnonInner$InnerOne: +method m1(Ljava/lang/String;)LObject;: +new *0: @A + +package: +class AnonInner$InnerOne$1: +method e1(Ljava/lang/Object;)LObject;: +return: @B + +package: +class AnonInner$InnerOne$1: +method e1(Ljava/lang/Object;)LObject;: +new *0: @C + +package: +class AnonInner$InnerOne: +method m2(Ljava/lang/String;)LObject;: +new *0: @D + +package: +class AnonInner$InnerOne$2: +method e2(Ljava/lang/Object;)LObject;: +return: @E + +package: +class AnonInner$InnerOne$2: +method e2(Ljava/lang/Object;)LObject;: +new *0: @F
diff --git a/annotation-file-utilities/tests/AnonInner.java b/annotation-file-utilities/tests/AnonInner.java new file mode 100644 index 0000000..599dd59 --- /dev/null +++ b/annotation-file-utilities/tests/AnonInner.java
@@ -0,0 +1,19 @@ +public class AnonInner { + class InnerOne { + Object m1(String p) { + return new Object() { + public Object e1(Object o) { + return new Object(); + } + }; + } + Object m2(String p) { + return new Object() { + public Object e2(Object o) { + return new Object(); + } + }; + } + + } +}
diff --git a/annotation-file-utilities/tests/ArrayLiteral.goal b/annotation-file-utilities/tests/ArrayLiteral.goal new file mode 100644 index 0000000..eb2209d --- /dev/null +++ b/annotation-file-utilities/tests/ArrayLiteral.goal
@@ -0,0 +1,11 @@ +class ArrayLiteral { + @checkers.inference.quals.VarAnnot(2) + int @checkers.inference.quals.VarAnnot(3) [] s0= ((new int @checkers.inference.quals.VarAnnot(1) [] {})); + @checkers.inference.quals.VarAnnot(5) + int @checkers.inference.quals.VarAnnot(6) [] s1 = ((new int @checkers.inference.quals.VarAnnot(4) [] {0, 1})); + @checkers.inference.quals.VarAnnot(8) + String @checkers.inference.quals.VarAnnot(9) [] s2 = ((new String @checkers.inference.quals.VarAnnot(7) [] {"TEST"})); + @checkers.inference.quals.VarAnnot(11) + String @checkers.inference.quals.VarAnnot(12) [] s3 = ((new String @checkers.inference.quals.VarAnnot(10) [] { "TEST" })); +} +
diff --git a/annotation-file-utilities/tests/ArrayLiteral.jaif b/annotation-file-utilities/tests/ArrayLiteral.jaif new file mode 100644 index 0000000..f46a8db --- /dev/null +++ b/annotation-file-utilities/tests/ArrayLiteral.jaif
@@ -0,0 +1,27 @@ +package checkers.inference.quals: + annotation @VarAnnot: + int value + +package : +class ArrayLiteral: + +field s0: +insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(1) +insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(2) +insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(3) + +field s1: +insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(4) +insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(5) +insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(6) + +field s2: +insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(7) +insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(8) +insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(9) + +field s3: +insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(10) +insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(11) +insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(12) +
diff --git a/annotation-file-utilities/tests/ArrayLiteral.java b/annotation-file-utilities/tests/ArrayLiteral.java new file mode 100644 index 0000000..33148af --- /dev/null +++ b/annotation-file-utilities/tests/ArrayLiteral.java
@@ -0,0 +1,7 @@ +class ArrayLiteral { + int[] s0= {}; + int[] s1 = {0, 1}; + String[] s2 = {"TEST"}; + String [] s3 = { "TEST" }; +} +
diff --git a/annotation-file-utilities/tests/ArrayMultiDim.goal b/annotation-file-utilities/tests/ArrayMultiDim.goal new file mode 100644 index 0000000..85e25a0 --- /dev/null +++ b/annotation-file-utilities/tests/ArrayMultiDim.goal
@@ -0,0 +1,9 @@ +package annotator.tests; + +import java.util.*; + +public class ArrayMultiDim { + @java.lang.C Object @java.lang.D [] @java.lang.E [] @java.lang.F [] field; + Object field2 = new @java.lang.C String @java.lang.D [1] @java.lang.E [2] @java.lang.F [3]; + @java.lang.C Object @java.lang.D [] @java.lang.E [] @java.lang.F [] field3 = new @java.lang.C Object @java.lang.D [1] @java.lang.E [2] @java.lang.F [3]; +}
diff --git a/annotation-file-utilities/tests/ArrayMultiDim.jaif b/annotation-file-utilities/tests/ArrayMultiDim.jaif new file mode 100644 index 0000000..3563ed9 --- /dev/null +++ b/annotation-file-utilities/tests/ArrayMultiDim.jaif
@@ -0,0 +1,34 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @H: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ArrayMultiDim: + + field field: + type: @D + inner-type 0, 0: @E + inner-type 0, 0, 0, 0: @F + inner-type 0, 0, 0, 0, 0, 0: @C + + field field2: + new *0: @D + inner-type 0, 0: @E + inner-type 0, 0, 0, 0: @F + inner-type 0, 0, 0, 0, 0, 0: @C + + field field3: + type: @D + inner-type 0, 0: @E + inner-type 0, 0, 0, 0: @F + inner-type 0, 0, 0, 0, 0, 0: @C + new *0: @D + inner-type 0, 0: @E + inner-type 0, 0, 0, 0: @F + inner-type 0, 0, 0, 0, 0, 0: @C
diff --git a/annotation-file-utilities/tests/ArrayMultiDim.java b/annotation-file-utilities/tests/ArrayMultiDim.java new file mode 100644 index 0000000..5a2b6b5 --- /dev/null +++ b/annotation-file-utilities/tests/ArrayMultiDim.java
@@ -0,0 +1,9 @@ +package annotator.tests; + +import java.util.*; + +public class ArrayMultiDim { + Object[][][] field; + Object field2 = new String[1][2][3]; + Object[][][] field3 = new Object[1][2][3]; +}
diff --git a/annotation-file-utilities/tests/ArrayParamSimple.goal b/annotation-file-utilities/tests/ArrayParamSimple.goal new file mode 100644 index 0000000..66ad0be --- /dev/null +++ b/annotation-file-utilities/tests/ArrayParamSimple.goal
@@ -0,0 +1,21 @@ +package annotator.tests; + +public class ArrayParamSimple { + + public void m1(@java.lang.Tainted Integer[] arg) { } + + public void m2(Integer @java.lang.Tainted [] arg) { } + + public void m3(@java.lang.Nullable Integer[] arg) { } + + public void m4(Integer @java.lang.Nullable [] arg) { } + + public void m5(Integer[] arg) { } + + public void m6(Integer[] arg) { } + + public void m7(Integer[] arg) { } + + public void m8(Integer[] arg) { } + +}
diff --git a/annotation-file-utilities/tests/ArrayParamSimple.jaif b/annotation-file-utilities/tests/ArrayParamSimple.jaif new file mode 100644 index 0000000..1f5bd02 --- /dev/null +++ b/annotation-file-utilities/tests/ArrayParamSimple.jaif
@@ -0,0 +1,36 @@ +package java.lang: + +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +// Note no @Target meta-annotation +annotation @Nullable: + +package annotator.tests: +class ArrayParamSimple: + + method <init>()V: + + method m1([Ljava/lang/Integer;)V: + parameter #0: + type: + inner-type 0, 0: @java.lang.Tainted + + method m2([Ljava/lang/Integer;)V: + parameter #0: + type: @java.lang.Tainted + inner-type 0, 0: + + method m3([Ljava/lang/Integer;)V: + parameter #0: + type: + inner-type 0, 0: @java.lang.Nullable + + method m4([Ljava/lang/Integer;)V: + parameter #0: + type: @java.lang.Nullable + inner-type 0, 0: + +// The tool properly issues a warning about inner-type on a declaration location. +// method m5([Ljava/lang/Integer;)V: +// parameter #0: +// inner-type 0, 0: @java.lang.Nullable
diff --git a/annotation-file-utilities/tests/ArrayParamSimple.java b/annotation-file-utilities/tests/ArrayParamSimple.java new file mode 100644 index 0000000..adf7470 --- /dev/null +++ b/annotation-file-utilities/tests/ArrayParamSimple.java
@@ -0,0 +1,21 @@ +package annotator.tests; + +public class ArrayParamSimple { + + public void m1(Integer[] arg) { } + + public void m2(Integer[] arg) { } + + public void m3(Integer[] arg) { } + + public void m4(Integer[] arg) { } + + public void m5(Integer[] arg) { } + + public void m6(Integer[] arg) { } + + public void m7(Integer[] arg) { } + + public void m8(Integer[] arg) { } + +}
diff --git a/annotation-file-utilities/tests/ArrayReturnTypeSimple.goal b/annotation-file-utilities/tests/ArrayReturnTypeSimple.goal new file mode 100644 index 0000000..1b5263d --- /dev/null +++ b/annotation-file-utilities/tests/ArrayReturnTypeSimple.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class ArrayReturnTypeSimple { + private @java.lang.UnderInitialization Object[] foo() { + return null; + } +}
diff --git a/annotation-file-utilities/tests/ArrayReturnTypeSimple.jaif b/annotation-file-utilities/tests/ArrayReturnTypeSimple.jaif new file mode 100644 index 0000000..2157d68 --- /dev/null +++ b/annotation-file-utilities/tests/ArrayReturnTypeSimple.jaif
@@ -0,0 +1,12 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ArrayReturnTypeSimple: + + method <init>()V: + + method foo()[Ljava/lang/Object;: + return: + inner-type 0, 0: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/ArrayReturnTypeSimple.java b/annotation-file-utilities/tests/ArrayReturnTypeSimple.java new file mode 100644 index 0000000..370a222 --- /dev/null +++ b/annotation-file-utilities/tests/ArrayReturnTypeSimple.java
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class ArrayReturnTypeSimple { + private Object[] foo() { + return null; + } +}
diff --git a/annotation-file-utilities/tests/BoundClassMultiple.goal b/annotation-file-utilities/tests/BoundClassMultiple.goal new file mode 100644 index 0000000..2e99ceb --- /dev/null +++ b/annotation-file-utilities/tests/BoundClassMultiple.goal
@@ -0,0 +1,11 @@ +package annotator.tests; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +public class BoundClassMultiple< + T extends @java.lang.A Date, + U extends @java.lang.B Object & @java.lang.C List & @java.lang.D Serializable, + V extends @java.lang.E Object & Comparable<@java.lang.F V>> { +}
diff --git a/annotation-file-utilities/tests/BoundClassMultiple.jaif b/annotation-file-utilities/tests/BoundClassMultiple.jaif new file mode 100644 index 0000000..9b3bce9 --- /dev/null +++ b/annotation-file-utilities/tests/BoundClassMultiple.jaif
@@ -0,0 +1,29 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundClassMultiple: + bound 0 & 0: @java.lang.A + bound 1 & 0: @java.lang.B + bound 1 & 1: @java.lang.C + bound 1 & 2: @java.lang.D + bound 2 & 0: @java.lang.E + inner-type 3, 0: @java.lang.F + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/BoundClassMultiple.java b/annotation-file-utilities/tests/BoundClassMultiple.java new file mode 100644 index 0000000..a7020ab --- /dev/null +++ b/annotation-file-utilities/tests/BoundClassMultiple.java
@@ -0,0 +1,11 @@ +package annotator.tests; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +public class BoundClassMultiple< + T extends Date, + U extends List & Serializable, + V extends Comparable<V>> { +}
diff --git a/annotation-file-utilities/tests/BoundClassSimple.goal b/annotation-file-utilities/tests/BoundClassSimple.goal new file mode 100644 index 0000000..6b2b267 --- /dev/null +++ b/annotation-file-utilities/tests/BoundClassSimple.goal
@@ -0,0 +1,8 @@ +package annotator.tests; + +import java.util.Date; + +public class BoundClassSimple<T extends @java.lang.Tainted Date> { + T field = null; + Date misleadingField = new Date(); +}
diff --git a/annotation-file-utilities/tests/BoundClassSimple.jaif b/annotation-file-utilities/tests/BoundClassSimple.jaif new file mode 100644 index 0000000..05edaf6 --- /dev/null +++ b/annotation-file-utilities/tests/BoundClassSimple.jaif
@@ -0,0 +1,12 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundClassSimple: + bound 0 & 0: @java.lang.Tainted + + field field: + field misleadingField: + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/BoundClassSimple.java b/annotation-file-utilities/tests/BoundClassSimple.java new file mode 100644 index 0000000..b2af877 --- /dev/null +++ b/annotation-file-utilities/tests/BoundClassSimple.java
@@ -0,0 +1,8 @@ +package annotator.tests; + +import java.util.Date; + +public class BoundClassSimple<T extends Date> { + T field = null; + Date misleadingField = new Date(); +}
diff --git a/annotation-file-utilities/tests/BoundMethodMultiple.goal b/annotation-file-utilities/tests/BoundMethodMultiple.goal new file mode 100644 index 0000000..32e2e56 --- /dev/null +++ b/annotation-file-utilities/tests/BoundMethodMultiple.goal
@@ -0,0 +1,24 @@ +package annotator.tests; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Date; + +public class BoundMethodMultiple { + public <T> void foo(@java.lang.A Object o) { + + } + + public <T extends @java.lang.B Date> void foo(T o) { + + } + + public <T extends @java.lang.C Object & @java.lang.D List & @java.lang.E Serializable> void foo(T t) { + + } + + public <T extends @java.lang.F Date, U extends @java.lang.G Map> void foo(T t, U u) { + + } +}
diff --git a/annotation-file-utilities/tests/BoundMethodMultiple.jaif b/annotation-file-utilities/tests/BoundMethodMultiple.jaif new file mode 100644 index 0000000..8dba019 --- /dev/null +++ b/annotation-file-utilities/tests/BoundMethodMultiple.jaif
@@ -0,0 +1,42 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundMethodMultiple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + parameter #0: + type: @java.lang.A + + method foo(Lannotator/tests/Date;)V: + bound 0 & 0: @java.lang.B + + method foo(Ljava/util/List;)V: + bound 0 & 0: @java.lang.C + bound 0 & 1: @java.lang.D + bound 0 & 2: @java.lang.E + + method foo(Lannotator/tests/Date;Ljava/util/Map;)V: + bound 0 & 0: @java.lang.F + bound 1 & 1: @java.lang.G +
diff --git a/annotation-file-utilities/tests/BoundMethodMultiple.java b/annotation-file-utilities/tests/BoundMethodMultiple.java new file mode 100644 index 0000000..8419d9d --- /dev/null +++ b/annotation-file-utilities/tests/BoundMethodMultiple.java
@@ -0,0 +1,24 @@ +package annotator.tests; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Date; + +public class BoundMethodMultiple { + public <T> void foo(Object o) { + + } + + public <T extends Date> void foo(T o) { + + } + + public <T extends List & Serializable> void foo(T t) { + + } + + public <T extends Date, U extends Map> void foo(T t, U u) { + + } +}
diff --git a/annotation-file-utilities/tests/BoundMethodSimple.goal b/annotation-file-utilities/tests/BoundMethodSimple.goal new file mode 100644 index 0000000..a5db040 --- /dev/null +++ b/annotation-file-utilities/tests/BoundMethodSimple.goal
@@ -0,0 +1,20 @@ +package annotator.tests; + +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +public class BoundMethodSimple { + public <T extends @java.lang.Tainted Date> void foo(T t) { + System.out.println(t); + } + + public <T extends @java.lang.Tainted Object> void foo2(T t) { + System.out.println(t); + } + + public static <T extends @org.checkerframework.checker.nullness.qual.Nullable @org.checkerframework.checker.nullness.qual.UnknownKeyFor Object> void + foo3(List<T> list, Comparator<? super T> c) { + } +} +
diff --git a/annotation-file-utilities/tests/BoundMethodSimple.jaif b/annotation-file-utilities/tests/BoundMethodSimple.jaif new file mode 100644 index 0000000..abefd4b --- /dev/null +++ b/annotation-file-utilities/tests/BoundMethodSimple.jaif
@@ -0,0 +1,21 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package org.checkerframework.checker.nullness.qual: +annotation @Nullable: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @UnknownKeyFor: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundMethodSimple: + + method <init>()V: + + method foo(Lannotator/tests/Date;)V: + bound 0 & 0: @java.lang.Tainted + + method foo2(Ljava/lang/Object;)V: + bound 0 & 0: @java.lang.Tainted + + method foo3(Ljava/util/List;Ljava/util/Comparator;)V: + bound 0 & 0: @org.checkerframework.checker.nullness.qual.Nullable @org.checkerframework.checker.nullness.qual.UnknownKeyFor +
diff --git a/annotation-file-utilities/tests/BoundMethodSimple.java b/annotation-file-utilities/tests/BoundMethodSimple.java new file mode 100644 index 0000000..b681ad4 --- /dev/null +++ b/annotation-file-utilities/tests/BoundMethodSimple.java
@@ -0,0 +1,20 @@ +package annotator.tests; + +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +public class BoundMethodSimple { + public <T extends Date> void foo(T t) { + System.out.println(t); + } + + public <T> void foo2(T t) { + System.out.println(t); + } + + public static <T> void + foo3(List<T> list, Comparator<? super T> c) { + } +} +
diff --git a/annotation-file-utilities/tests/BoundZero.goal b/annotation-file-utilities/tests/BoundZero.goal new file mode 100644 index 0000000..5b3fe66 --- /dev/null +++ b/annotation-file-utilities/tests/BoundZero.goal
@@ -0,0 +1,28 @@ +package annotator.tests; + +import static java.lang.annotation.ElementType.TYPE_USE; +import java.lang.annotation.Target; + +//@skip-test +@Target({TYPE_USE}) @interface A {} +@Target({TYPE_USE}) @interface B {} +@Target({TYPE_USE}) @interface C {} +@Target({TYPE_USE}) @interface X {} +@Target({TYPE_USE}) @interface Y {} + +class BoundZero<U extends @annotator.tests.A @annotator.tests.B @annotator.tests.C Object> { + <T extends @annotator.tests.X @annotator.tests.Y Object> void m1(T o) {} + <T extends @Y @annotator.tests.X Object & Comparable<T>> void m2(T o) {} + <T extends @annotator.tests.Y @annotator.tests.X Object & Comparable<T>> void m3(T o) {} + <T extends java.lang.@annotator.tests.X @annotator.tests.Y Object & Comparable<T>> void m4(T o) {} + <T extends java.lang.@Y @annotator.tests.X Object & Comparable<T>> void m5(T o) {} + <T extends java.lang.@annotator.tests.Y @annotator.tests.X Object & Comparable<T>> void m6(T o) {} + <T extends @annotator.tests.X Object & @annotator.tests.Y Comparable<T>> void m7(T o) {} + <T extends @annotator.tests.X Object & @Y Comparable<T>> void m8(T o) {} + <T extends @annotator.tests.X Object & @annotator.tests.Y Comparable<T>> void m9(T o) {} + class Inner<@X V extends @Y @annotator.tests.A @annotator.tests.B @annotator.tests.C Object> {} +} + +class Outer<S extends @annotator.tests.A @annotator.tests.B @annotator.tests.C Object & java.io.Serializable> { +} +
diff --git a/annotation-file-utilities/tests/BoundZero.jaif b/annotation-file-utilities/tests/BoundZero.jaif new file mode 100644 index 0000000..d97761f --- /dev/null +++ b/annotation-file-utilities/tests/BoundZero.jaif
@@ -0,0 +1,44 @@ +package annotator.tests: +annotation @A: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) +annotation @B: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) +annotation @C: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) +annotation @X: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) +annotation @Y: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) + +class BoundZero: + bound 0&0: @A @B @C + + method m1(Ljava/lang/Object;)V: + bound 0&0: @X @Y + + method m2(Ljava/lang/Object;)V: + bound 0&0: @X + + method m3(Ljava/lang/Object;)V: + bound 0&0: @X + + method m4(Ljava/lang/Object;)V: + bound 0&0: @X @Y + + method m5(Ljava/lang/Object;)V: + bound 0&0: @X + + method m6(Ljava/lang/Object;)V: + bound 0&0: @X + + method m7(Ljava/lang/Object;)V: + bound 0&0: @X + bound 0&1: @Y + + method m8(Ljava/lang/Object;)V: + bound 0&0: @X + + method m9(Ljava/lang/Object;)V: + bound 0&0: @X + +class BoundZero$Inner: + bound 0&0: @A @B @C + +class Outer: + bound 0&0: @A @B @C +
diff --git a/annotation-file-utilities/tests/BoundZero.java b/annotation-file-utilities/tests/BoundZero.java new file mode 100644 index 0000000..4b1a4c8 --- /dev/null +++ b/annotation-file-utilities/tests/BoundZero.java
@@ -0,0 +1,27 @@ +package annotator.tests; + +import static java.lang.annotation.ElementType.TYPE_USE; +import java.lang.annotation.Target; + +@Target({TYPE_USE}) @interface A {} +@Target({TYPE_USE}) @interface B {} +@Target({TYPE_USE}) @interface C {} +@Target({TYPE_USE}) @interface X {} +@Target({TYPE_USE}) @interface Y {} + +class BoundZero<U> { + <T extends Object> void m1(T o) {} + <T extends @Y Object & Comparable<T>> void m2(T o) {} + <T extends @annotator.tests.Y Object & Comparable<T>> void m3(T o) {} + <T extends java.lang.Object & Comparable<T>> void m4(T o) {} + <T extends java.lang.@Y Object & Comparable<T>> void m5(T o) {} + <T extends java.lang.@annotator.tests.Y Object & Comparable<T>> void m6(T o) {} + <T extends Comparable<T>> void m7(T o) {} + <T extends @Y Comparable<T>> void m8(T o) {} + <T extends @annotator.tests.Y Comparable<T>> void m9(T o) {} + class Inner<@X V extends @Y Object> {} +} + +class Outer<S extends java.io.Serializable> { +} +
diff --git a/annotation-file-utilities/tests/CastInsert.goal b/annotation-file-utilities/tests/CastInsert.goal new file mode 100644 index 0000000..447ce9a --- /dev/null +++ b/annotation-file-utilities/tests/CastInsert.goal
@@ -0,0 +1,89 @@ +import java.io.PrintStream; + +public class CastInsert { + + PrintStream out; + private int c = ((@checkers.nullness.quals.Nullable Integer) (((@checkers.nullness.quals.Nullable Integer) (12)) + ((@checkers.nullness.quals.Nullable Integer) (13)))); + private String str = "this" + ((@checkers.nullness.quals.Nullable String) ("is")).concat(((@checkers.nullness.quals.Nullable String) ("string"))); + + void m() { + int i; + } + + int m(String y, String[] z, int i) { + String x = ((@checkers.nullness.quals.Nullable String) (new String())); + String s; + s = ((@checkers.nullness.quals.Nullable String) (((@checkers.nullness.quals.Nullable String) (x)) + ((@checkers.nullness.quals.Nullable String) (x)))); + s = y; + s = z[((@checkers.nullness.quals.Nullable Integer) (0))]; + s = x; + int j = 0; + switch (((@checkers.nullness.quals.Nullable Integer) (i + 2))) { + case 1: + j = i + ((@checkers.nullness.quals.Nullable Integer) (1)); + System.out.println(1); + break; + case 2: + j = i + 2; + System.out.println(((@checkers.nullness.quals.Nullable Integer) (2))); + break; + default: + j = i + 3; + System.out.println(-1); + } + j *= ((@checkers.nullness.quals.Nullable Integer) (i)); + j = s != x ? ((@checkers.nullness.quals.Nullable Integer) (j)) : i; + do { + int h = ((@checkers.nullness.quals.Nullable Integer) (i)) & j; + } while (i < j); + for (int i2 : new int[5]) { + j = ((@checkers.nullness.quals.Nullable Integer) (i2)); + } + for (int a = 0, b = ((@checkers.nullness.quals.Nullable Integer) (0)); a < ((@checkers.nullness.quals.Nullable Integer) (j)); a = ((@checkers.nullness.quals.Nullable Integer) (a + 1)), b++) + a = ((@checkers.nullness.quals.Nullable Integer) (b)); + if (((@checkers.nullness.quals.Nullable Integer) (i)) < j) + i = j; + else + j = ((@checkers.nullness.quals.Nullable Integer) (i)); + boolean b = ((@checkers.nullness.quals.Nullable String) (x)) instanceof String; + label: b = ((@checkers.nullness.quals.Nullable Boolean) (false)); + Object o = ((@checkers.nullness.quals.Nullable CastInsert) (this)).out; + m(y, z, ((@checkers.nullness.quals.Nullable Integer) (i))); + int[][] test = new int[4][((@checkers.nullness.quals.Nullable Integer) (5))]; + int[][] test2 = {{1, 2}, {1, 2, ((@checkers.nullness.quals.Nullable Integer) (3))}}; + new String(((@checkers.nullness.quals.Nullable String) ("test"))); + if (i < 1) + return ((@checkers.nullness.quals.Nullable Integer) (18)); + synchronized (o) { + i = ((@checkers.nullness.quals.Nullable Integer) (i + i)); + } + if (j < 1) + throw ((@checkers.nullness.quals.Nullable RuntimeException) (new IllegalStateException())); + try { + int t = ((@checkers.nullness.quals.Nullable Integer) (1)); + } catch (Error e) { + i = j; + } catch (RuntimeException e) { + j = ((@checkers.nullness.quals.Nullable Integer) (i)); + } finally { + j = i + ((@checkers.nullness.quals.Nullable Integer) (j)); + } + j = (int) (i + ((@checkers.nullness.quals.Nullable Integer) (j))); + j = - ((@checkers.nullness.quals.Nullable Integer) (j)); + while (i < ((@checkers.nullness.quals.Nullable Integer) (j))) + i = ((@checkers.nullness.quals.Nullable Integer) (i)) + 1; + ((@checkers.nullness.quals.Nullable CastInsert) (this)).out.println(); + ((@checkers.nullness.quals.Nullable PrintStream) (System.out)).println(); + Object obj = ((@checkers.nullness.quals.Nullable @annotation.A @annotation.B @annotation.C String) (null)); + return 0; + } + + void m2 (Object o) { + o = ((@checkers.nullness.quals.Nullable Map<@annotation.A String, @annotation.B Integer>) (o)); + o = ((@annotation.B Object @annotation.A [] @checkers.nullness.quals.Nullable []) (o)); + o = ((@annotation.B int @annotation.A [] @checkers.nullness.quals.Nullable []) (o)); + o = ((@checkers.nullness.quals.Nullable List<@annotation.A ? extends @annotation.B Object>) (o)); + o = ((int) (o)); + o = ((Map<@annotation.A String, @annotation.B Integer>) (o)); + } +}
diff --git a/annotation-file-utilities/tests/CastInsert.jaif b/annotation-file-utilities/tests/CastInsert.jaif new file mode 100644 index 0000000..4797cd4 --- /dev/null +++ b/annotation-file-utilities/tests/CastInsert.jaif
@@ -0,0 +1,77 @@ +package annotation: +annotation @A: +annotation @B: +annotation @C: + +package checkers.nullness.quals: +annotation @Nullable: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) + +package : +class CastInsert: + + field c: + insert-typecast Variable.initializer, Binary.rightOperand: @Nullable Integer + insert-typecast Variable.initializer, Binary.leftOperand: @Nullable Integer + insert-typecast Variable.initializer: @Nullable Integer + + field str: + insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable String + insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @Nullable String + + method m(Ljava/lang/String;[Ljava/lang/String;I)I: + insert-typecast Block.statement 0, Variable.initializer: @Nullable String + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @Nullable String + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable String + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable String + insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @Nullable Integer + insert-typecast Block.statement 7, Switch.expression, Parenthesized.expression: @Nullable Integer + insert-typecast Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable Integer + insert-typecast Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @Nullable Integer + insert-typecast Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @Nullable Integer + insert-typecast Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @Nullable Integer + insert-typecast Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @Nullable Integer + insert-typecast Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer + insert-typecast Block.statement 12, ForLoop.initializer 1, Variable.initializer: @Nullable Integer + insert-typecast Block.statement 12, ForLoop.condition, Binary.rightOperand: @Nullable Integer + insert-typecast Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer + insert-typecast Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @Nullable Integer + insert-typecast Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @Nullable Integer + insert-typecast Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @Nullable Integer + insert-typecast Block.statement 14, Variable.initializer, InstanceOf.expression: @Nullable String + insert-typecast Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @Nullable Boolean + insert-typecast Block.statement 16, Variable.initializer, MemberSelect.expression: @Nullable CastInsert + insert-typecast Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @Nullable Integer + insert-typecast Block.statement 18, Variable.initializer, NewArray.dimension 1: @Nullable Integer + insert-typecast Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @Nullable Integer + insert-typecast Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @Nullable String + insert-typecast Block.statement 21, If.thenStatement, Return.expression: @Nullable Integer + insert-typecast Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer + insert-typecast Block.statement 23, If.thenStatement, Throw.expression: @Nullable RuntimeException + insert-typecast Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @Nullable Integer + insert-typecast Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer + insert-typecast Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable Integer + insert-typecast Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @Nullable Integer + insert-typecast Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @Nullable Integer + insert-typecast Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @Nullable Integer + insert-typecast Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable Integer + insert-typecast Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @Nullable CastInsert + insert-typecast Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable PrintStream + insert-typecast Block.statement 30, Variable.initializer: @Nullable @A @B @C String + + method m2(Ljava/lang/Object;)V: + insert-typecast Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Map<String, Integer> + inner-type 3, 0: @A + inner-type 3, 1: @B + insert-typecast Block.statement 1, ExpressionStatement.expression, Assignment.expression: @Nullable Object[][] + inner-type 0, 0: @A + inner-type 0, 0, 0, 0: @B + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @Nullable int[][] + inner-type 0, 0: @A + inner-type 0, 0, 0, 0: @B + insert-typecast Block.statement 3, ExpressionStatement.expression, Assignment.expression: @Nullable List<? extends Object> + inner-type 3, 0: @A + inner-type 3, 0, 2, 0: @B + insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression: int + insert-typecast Block.statement 5, ExpressionStatement.expression, Assignment.expression: Map<String, Integer> + inner-type 3, 0: @A + inner-type 3, 1: @B
diff --git a/annotation-file-utilities/tests/CastInsert.java b/annotation-file-utilities/tests/CastInsert.java new file mode 100644 index 0000000..ea066b6 --- /dev/null +++ b/annotation-file-utilities/tests/CastInsert.java
@@ -0,0 +1,89 @@ +import java.io.PrintStream; + +public class CastInsert { + + PrintStream out; + private int c = 12 + 13; + private String str = "this" + "is".concat("string"); + + void m() { + int i; + } + + int m(String y, String[] z, int i) { + String x = new String(); + String s; + s = x + x; + s = y; + s = z[0]; + s = x; + int j = 0; + switch (i + 2) { + case 1: + j = i + 1; + System.out.println(1); + break; + case 2: + j = i + 2; + System.out.println(2); + break; + default: + j = i + 3; + System.out.println(-1); + } + j *= i; + j = s != x ? j : i; + do { + int h = i & j; + } while (i < j); + for (int i2 : new int[5]) { + j = i2; + } + for (int a = 0, b = 0; a < j; a = a + 1, b++) + a = b; + if (i < j) + i = j; + else + j = i; + boolean b = x instanceof String; + label: b = false; + Object o = this.out; + m(y, z, i); + int[][] test = new int[4][5]; + int[][] test2 = {{1, 2}, {1, 2, 3}}; + new String("test"); + if (i < 1) + return 18; + synchronized (o) { + i = i + i; + } + if (j < 1) + throw new IllegalStateException(); + try { + int t = 1; + } catch (Error e) { + i = j; + } catch (RuntimeException e) { + j = i; + } finally { + j = i + j; + } + j = (int) (i + j); + j = -j; + while (i < j) + i = i + 1; + this.out.println(); + System.out.println(); + Object obj = null; + return 0; + } + + void m2 (Object o) { + o = o; + o = o; + o = o; + o = o; + o = o; + o = o; + } +}
diff --git a/annotation-file-utilities/tests/ClassAnnotationParameter.goal b/annotation-file-utilities/tests/ClassAnnotationParameter.goal new file mode 100644 index 0000000..76e8bd4 --- /dev/null +++ b/annotation-file-utilities/tests/ClassAnnotationParameter.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class ClassAnnotationParameter { + @java.lang.TestAnnotation(java.lang.Object.class) + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassAnnotationParameter.jaif b/annotation-file-utilities/tests/ClassAnnotationParameter.jaif new file mode 100644 index 0000000..96ccf7f --- /dev/null +++ b/annotation-file-utilities/tests/ClassAnnotationParameter.jaif
@@ -0,0 +1,9 @@ +package java.lang: + +annotation @TestAnnotation: + Class value + +package annotator.tests: +class ClassAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value=java.lang.Object.class) \ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassAnnotationParameter.java b/annotation-file-utilities/tests/ClassAnnotationParameter.java new file mode 100644 index 0000000..993aa38 --- /dev/null +++ b/annotation-file-utilities/tests/ClassAnnotationParameter.java
@@ -0,0 +1,6 @@ +package annotator.tests; + +public class ClassAnnotationParameter { + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassListAnnotationParameter.goal b/annotation-file-utilities/tests/ClassListAnnotationParameter.goal new file mode 100644 index 0000000..4a41594 --- /dev/null +++ b/annotation-file-utilities/tests/ClassListAnnotationParameter.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class ClassListAnnotationParameter { + @java.lang.TestAnnotation({java.lang.Object.class,java.lang.String.class}) + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassListAnnotationParameter.jaif b/annotation-file-utilities/tests/ClassListAnnotationParameter.jaif new file mode 100644 index 0000000..b971989 --- /dev/null +++ b/annotation-file-utilities/tests/ClassListAnnotationParameter.jaif
@@ -0,0 +1,9 @@ +package java.lang: + +annotation @TestAnnotation: + Class[] value + +package annotator.tests: +class ClassListAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value={java.lang.Object.class, java.lang.String.class})
diff --git a/annotation-file-utilities/tests/ClassListAnnotationParameter.java b/annotation-file-utilities/tests/ClassListAnnotationParameter.java new file mode 100644 index 0000000..0ad674b --- /dev/null +++ b/annotation-file-utilities/tests/ClassListAnnotationParameter.java
@@ -0,0 +1,6 @@ +package annotator.tests; + +public class ClassListAnnotationParameter { + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassSimple.goal b/annotation-file-utilities/tests/ClassSimple.goal new file mode 100644 index 0000000..05707b2 --- /dev/null +++ b/annotation-file-utilities/tests/ClassSimple.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.Date; + +@java.lang.Tainted public class ClassSimple { + public Integer field; +}
diff --git a/annotation-file-utilities/tests/ClassSimple.jaif b/annotation-file-utilities/tests/ClassSimple.jaif new file mode 100644 index 0000000..8e51dac --- /dev/null +++ b/annotation-file-utilities/tests/ClassSimple.jaif
@@ -0,0 +1,9 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @DoesNotExist: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ClassSimple: @java.lang.Tainted + +package: +class ClassSimple: @java.lang.DoesNotExist
diff --git a/annotation-file-utilities/tests/ClassSimple.java b/annotation-file-utilities/tests/ClassSimple.java new file mode 100644 index 0000000..e95912c --- /dev/null +++ b/annotation-file-utilities/tests/ClassSimple.java
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.Date; + +public class ClassSimple { + public Integer field; +}
diff --git a/annotation-file-utilities/tests/ComplexLocationOne.goal b/annotation-file-utilities/tests/ComplexLocationOne.goal new file mode 100644 index 0000000..dc26a83 --- /dev/null +++ b/annotation-file-utilities/tests/ComplexLocationOne.goal
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; +import java.util.Map; + +public class ComplexLocationOne { + public @java.lang.A List< @java.lang.B Map<@java.lang.C Integer, @java.lang.D String @java.lang.E []>> field; + public List< Outer<@java.lang.A Integer, @java.lang.B String[]>.@java.lang.C Inner<@java.lang.D Integer, @java.lang.E String[]>> entries; + class Outer<W, X> { class Inner<Y, Z> {} } +}
diff --git a/annotation-file-utilities/tests/ComplexLocationOne.jaif b/annotation-file-utilities/tests/ComplexLocationOne.jaif new file mode 100644 index 0000000..c94733e --- /dev/null +++ b/annotation-file-utilities/tests/ComplexLocationOne.jaif
@@ -0,0 +1,33 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ComplexLocationOne: + + field field: + type: @java.lang.A + inner-type 3, 0: @java.lang.B + inner-type 3, 0, 3, 0: @java.lang.C + inner-type 3, 0, 3, 1, 0, 0: @java.lang.D + inner-type 3, 0, 3, 1: @java.lang.E + + field entries: + type: + inner-type 3, 0, 3, 0: @java.lang.A + inner-type 3, 0, 3, 1, 0, 0: @java.lang.B + inner-type 3, 0, 1, 0: @java.lang.C + inner-type 3, 0, 1, 0, 3, 0: @java.lang.D + inner-type 3, 0, 1, 0, 3, 1, 0, 0: @java.lang.E +
diff --git a/annotation-file-utilities/tests/ComplexLocationOne.java b/annotation-file-utilities/tests/ComplexLocationOne.java new file mode 100644 index 0000000..9e45d45 --- /dev/null +++ b/annotation-file-utilities/tests/ComplexLocationOne.java
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; +import java.util.Map; + +public class ComplexLocationOne { + public List< Map<Integer, String []>> field; + public List< Outer<Integer, String[]>.Inner<Integer, String[]>> entries; + class Outer<W, X> { class Inner<Y, Z> {} } +}
diff --git a/annotation-file-utilities/tests/ComplexLocationTwo.goal b/annotation-file-utilities/tests/ComplexLocationTwo.goal new file mode 100644 index 0000000..5be0c43 --- /dev/null +++ b/annotation-file-utilities/tests/ComplexLocationTwo.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.*; + +public class ComplexLocationTwo { + @java.lang.A Map<@java.lang.B Comparable<@java.lang.C Object @java.lang.D [] @java.lang.E [] @java.lang.F []>, @java.lang.G List<@java.lang.H Date>> field; +}
diff --git a/annotation-file-utilities/tests/ComplexLocationTwo.jaif b/annotation-file-utilities/tests/ComplexLocationTwo.jaif new file mode 100644 index 0000000..1b0255d --- /dev/null +++ b/annotation-file-utilities/tests/ComplexLocationTwo.jaif
@@ -0,0 +1,22 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @H: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ComplexLocationTwo: + + field field: + type: @java.lang.A + inner-type 3, 0: @B + inner-type 3, 0, 3, 0: @D + inner-type 3, 0, 3, 0, 0, 0: @E + inner-type 3, 0, 3, 0, 0, 0, 0, 0: @F + inner-type 3, 0, 3, 0, 0, 0, 0, 0, 0, 0: @C + inner-type 3, 1: @G + inner-type 3, 1, 3, 0: @H
diff --git a/annotation-file-utilities/tests/ComplexLocationTwo.java b/annotation-file-utilities/tests/ComplexLocationTwo.java new file mode 100644 index 0000000..c27f96d --- /dev/null +++ b/annotation-file-utilities/tests/ComplexLocationTwo.java
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.*; + +public class ComplexLocationTwo { + Map<Comparable<Object[][][]>, List<Date>> field; +}
diff --git a/annotation-file-utilities/tests/ConstructorParam.goal b/annotation-file-utilities/tests/ConstructorParam.goal new file mode 100644 index 0000000..580b999 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorParam.goal
@@ -0,0 +1,9 @@ +package annotator.tests; + +import java.util.List; + +public class ConstructorParam { + public ConstructorParam(@java.lang.UnderInitialization int paramB) { + + } +}
diff --git a/annotation-file-utilities/tests/ConstructorParam.jaif b/annotation-file-utilities/tests/ConstructorParam.jaif new file mode 100644 index 0000000..d58b454 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorParam.jaif
@@ -0,0 +1,11 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ConstructorParam: + + method <init>(I)V: + parameter #0: + type: @java.lang.UnderInitialization + +
diff --git a/annotation-file-utilities/tests/ConstructorParam.java b/annotation-file-utilities/tests/ConstructorParam.java new file mode 100644 index 0000000..d459c81 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorParam.java
@@ -0,0 +1,9 @@ +package annotator.tests; + +import java.util.List; + +public class ConstructorParam { + public ConstructorParam(int paramB) { + + } +}
diff --git a/annotation-file-utilities/tests/ConstructorParamMultiple.goal b/annotation-file-utilities/tests/ConstructorParamMultiple.goal new file mode 100644 index 0000000..6c2b20b --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorParamMultiple.goal
@@ -0,0 +1,12 @@ +package annotator.tests; + +import java.util.List; + +public class ConstructorParamMultiple { + public ConstructorParamMultiple( + /* @Tainted*/ @java.lang.Tainted Object a, + /* @Tainted*/ @java.lang.Tainted List</* @UnderInitialization*/ @java.lang.UnderInitialization Integer> b, + /* @Tainted*/ @java.lang.Tainted int c) { + + } +}
diff --git a/annotation-file-utilities/tests/ConstructorParamMultiple.jaif b/annotation-file-utilities/tests/ConstructorParamMultiple.jaif new file mode 100644 index 0000000..e6049c9 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorParamMultiple.jaif
@@ -0,0 +1,18 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ConstructorParamMultiple: + + method <init>(Ljava/lang/Object;Ljava/util/List;I)V: + parameter #0: + type: @java.lang.Tainted + parameter #1: + type: @java.lang.Tainted + inner-type 3, 0: @java.lang.UnderInitialization + parameter #2: + type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/ConstructorParamMultiple.java b/annotation-file-utilities/tests/ConstructorParamMultiple.java new file mode 100644 index 0000000..060c394 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorParamMultiple.java
@@ -0,0 +1,12 @@ +package annotator.tests; + +import java.util.List; + +public class ConstructorParamMultiple { + public ConstructorParamMultiple( + /* @Tainted*/ Object a, + /* @Tainted*/ List</* @UnderInitialization*/ Integer> b, + /* @Tainted*/ int c) { + + } +}
diff --git a/annotation-file-utilities/tests/ConstructorReceivers.goal b/annotation-file-utilities/tests/ConstructorReceivers.goal new file mode 100644 index 0000000..7c0954d --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorReceivers.goal
@@ -0,0 +1,58 @@ +package annotator.tests; + +public class ConstructorReceivers { + class C0 { + public C0(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this) {} + public C0(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this, int i) {} + } + + class C1 { + public C1(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this) {} + public C1(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this, int i) {} + } + + class C2 { + public C2(@checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C ConstructorReceivers ConstructorReceivers.this) {} + } + + class C3 { + public C3(@checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C ConstructorReceivers ConstructorReceivers.this) {} + } + + class P0<K, V> { + class C4 { + public C4(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {} + + public C4(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, int i) {} + } + + class C5 { + public C5(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {} + + public C5(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, int i) {} + } + + class C6 { + public C6(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {} + + public C6(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P0.this, ConstructorReceivers.P0<K, V> other) {} + } + + class C7 { + public C7(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {} + + public C7(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P0.this, ConstructorReceivers.P0<K, V> other) {} + } + } + + class P1<K extends Object, V> { + class C8 { + public C8(@checkers.tainting.quals.Tainted ConstructorReceivers.P1<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P1.this) {} + } + + class C9 { + public C9(@checkers.tainting.quals.Tainted ConstructorReceivers.P1<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P1.this) {} + } + } +} +
diff --git a/annotation-file-utilities/tests/ConstructorReceivers.jaif b/annotation-file-utilities/tests/ConstructorReceivers.jaif new file mode 100644 index 0000000..097a0b0 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorReceivers.jaif
@@ -0,0 +1,142 @@ +package annotation: +annotation @A: +annotation @B: +annotation @C: +annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package checkers.tainting.quals: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) + +package annotator.tests: +class ConstructorReceivers: + + method <init>()V: + return: + +class ConstructorReceivers$C0: + + field this$0: + + method <init>()V: + return: + receiver: @Tainted + + method <init>(I)V: + return: + receiver: @Tainted + +class ConstructorReceivers$C1: + + field this$0: + + method <init>()V: + return: + receiver: @Tainted + + method <init>(I)V: + return: + receiver: @Tainted + +class ConstructorReceivers$C2: + + field this$0: + + method <init>()V: + return: + receiver: @Tainted @A @B @C + +class ConstructorReceivers$C3: + + field this$0: + + method <init>()V: + return: + receiver: @Tainted @A @B @C + +class ConstructorReceivers$P0$C4: + + field this$1: + + method <init>()V: + return: + receiver: @Tainted + + method <init>(I)V: + return: + receiver: @Tainted + +class ConstructorReceivers$P0$C5: + + field this$1: + + method <init>()V: + return: + receiver: @Tainted + + method <init>(I)V: + return: + receiver: @Tainted + +class ConstructorReceivers$P0$C6: + + field this$1: + + method <init>()V: + return: + receiver: @Tainted + + method <init>(Lannotator/tests/ConstructorReceivers$P0;)V: + return: + receiver: @Tainted + inner-type 1, 0, 3, 0: @Inner(0) + inner-type 1, 0, 3, 1: @Inner(1) + +class ConstructorReceivers$P0$C7: + + field this$1: + + method <init>()V: + return: + receiver: @Tainted + + method <init>(Lannotator/tests/ConstructorReceivers$P0;)V: + return: + receiver: @Tainted + inner-type 1, 0, 3, 0: @Inner(0) + inner-type 1, 0, 3, 1: @Inner(1) + +class ConstructorReceivers$P0: + + field this$0: + + method <init>()V: + return: + +class ConstructorReceivers$P1$C8: + + field this$1: + + method <init>()V: + return: + receiver: @Tainted + inner-type 1, 0, 3, 0: @Inner(0) + inner-type 1, 0, 3, 1: @Inner(1) + +class ConstructorReceivers$P1$C9: + + field this$1: + + method <init>()V: + return: + receiver: @Tainted + inner-type 1, 0, 3, 0: @Inner(0) + inner-type 1, 0, 3, 1: @Inner(1) + +class ConstructorReceivers$P1: + + field this$0: + + method <init>()V: + return: +
diff --git a/annotation-file-utilities/tests/ConstructorReceivers.java b/annotation-file-utilities/tests/ConstructorReceivers.java new file mode 100644 index 0000000..03b0776 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorReceivers.java
@@ -0,0 +1,58 @@ +package annotator.tests; + +public class ConstructorReceivers { + class C0 { + public C0() {} + public C0(int i) {} + } + + class C1 { + public C1(ConstructorReceivers ConstructorReceivers.this) {} + public C1(ConstructorReceivers ConstructorReceivers.this, int i) {} + } + + class C2 { + public C2() {} + } + + class C3 { + public C3(ConstructorReceivers ConstructorReceivers.this) {} + } + + class P0<K, V> { + class C4 { + public C4() {} + + public C4(int i) {} + } + + class C5 { + public C5(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {} + + public C5(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, int i) {} + } + + class C6 { + public C6() {} + + public C6(ConstructorReceivers.P0<K, V> other) {} + } + + class C7 { + public C7(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {} + + public C7(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, ConstructorReceivers.P0<K, V> other) {} + } + } + + class P1<K extends Object, V> { + class C8 { + public C8() {} + } + + class C9 { + public C9(ConstructorReceivers.P1<K, V> ConstructorReceivers.P1.this) {} + } + } +} +
diff --git a/annotation-file-utilities/tests/ConstructorReturn.goal b/annotation-file-utilities/tests/ConstructorReturn.goal new file mode 100644 index 0000000..6e4c6d9 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorReturn.goal
@@ -0,0 +1,6 @@ +package annotator.tests; + +class ConstructorReturn { + public @java.lang.Tainted ConstructorReturn() { } +} +
diff --git a/annotation-file-utilities/tests/ConstructorReturn.jaif b/annotation-file-utilities/tests/ConstructorReturn.jaif new file mode 100644 index 0000000..2c7a752 --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorReturn.jaif
@@ -0,0 +1,8 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ConstructorReturn: + method ConstructorReturn()V: +return: @Tainted +
diff --git a/annotation-file-utilities/tests/ConstructorReturn.java b/annotation-file-utilities/tests/ConstructorReturn.java new file mode 100644 index 0000000..8d8c0ad --- /dev/null +++ b/annotation-file-utilities/tests/ConstructorReturn.java
@@ -0,0 +1,6 @@ +package annotator.tests; + +class ConstructorReturn { + public ConstructorReturn() { } +} +
diff --git a/annotation-file-utilities/tests/Date.goal b/annotation-file-utilities/tests/Date.goal new file mode 100644 index 0000000..a8b960d --- /dev/null +++ b/annotation-file-utilities/tests/Date.goal
@@ -0,0 +1,5 @@ +package annotator.tests; + +public class Date { + private long t; +}
diff --git a/annotation-file-utilities/tests/Date.jaif b/annotation-file-utilities/tests/Date.jaif new file mode 100644 index 0000000..a2007c7 --- /dev/null +++ b/annotation-file-utilities/tests/Date.jaif
@@ -0,0 +1,7 @@ +package annotator.tests: +class Date: + + field t: + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/Date.java b/annotation-file-utilities/tests/Date.java new file mode 100644 index 0000000..a8b960d --- /dev/null +++ b/annotation-file-utilities/tests/Date.java
@@ -0,0 +1,5 @@ +package annotator.tests; + +public class Date { + private long t; +}
diff --git a/annotation-file-utilities/tests/DeclarationAnnotation.goal b/annotation-file-utilities/tests/DeclarationAnnotation.goal new file mode 100644 index 0000000..ef31041 --- /dev/null +++ b/annotation-file-utilities/tests/DeclarationAnnotation.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class DeclarationAnnotation { + @java.lang.SuppressWarnings("string argument") + public void foo() { + } +}
diff --git a/annotation-file-utilities/tests/DeclarationAnnotation.jaif b/annotation-file-utilities/tests/DeclarationAnnotation.jaif new file mode 100644 index 0000000..ae42163 --- /dev/null +++ b/annotation-file-utilities/tests/DeclarationAnnotation.jaif
@@ -0,0 +1,10 @@ +package java.lang: + +annotation @SuppressWarnings: + String value + +package annotator.tests: +class DeclarationAnnotation: + + method foo()V: @java.lang.SuppressWarnings("string argument") +
diff --git a/annotation-file-utilities/tests/DeclarationAnnotation.java b/annotation-file-utilities/tests/DeclarationAnnotation.java new file mode 100644 index 0000000..da33178 --- /dev/null +++ b/annotation-file-utilities/tests/DeclarationAnnotation.java
@@ -0,0 +1,6 @@ +package annotator.tests; + +public class DeclarationAnnotation { + public void foo() { + } +}
diff --git a/annotation-file-utilities/tests/DuplicateAnnotation.goal b/annotation-file-utilities/tests/DuplicateAnnotation.goal new file mode 100644 index 0000000..7192e8d --- /dev/null +++ b/annotation-file-utilities/tests/DuplicateAnnotation.goal
@@ -0,0 +1,32 @@ +package annotator.tests; + +// This test ensures that no annotation is re-inserted, if it already +// existed in the original source code. +public class DuplicateAnnotation { + + @SuppressWarnings("A") + void m1() { } + + @java.lang.SuppressWarnings("B") + void m2() { } + + /*@SuppressWarnings("C")*/ + void m3() { } + + /*@java.lang.SuppressWarnings("D")*/ + void m4() { } + + @java.lang.SuppressWarnings("E") + void m5() { } + + void m6() { + @SuppressWarnings("F") + @java.lang.Deprecated + Object o = new Object(); + } + + int /*@annotator.tests.TA*/ [] i; +} + +@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) +@interface TA {}
diff --git a/annotation-file-utilities/tests/DuplicateAnnotation.jaif b/annotation-file-utilities/tests/DuplicateAnnotation.jaif new file mode 100644 index 0000000..a5516e7 --- /dev/null +++ b/annotation-file-utilities/tests/DuplicateAnnotation.jaif
@@ -0,0 +1,19 @@ +package java.lang: +annotation @SuppressWarnings: + String value +annotation @Deprecated: + +package annotator.tests: +annotation @TA: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) + +class DuplicateAnnotation: + field i: + type: @TA + + method m1()V: @java.lang.SuppressWarnings("A") + method m2()V: @java.lang.SuppressWarnings("B") + method m3()V: @java.lang.SuppressWarnings("C") + method m4()V: @java.lang.SuppressWarnings("D") + method m5()V: @java.lang.SuppressWarnings("E") + method m6()V: + local o: @java.lang.SuppressWarnings("F") @java.lang.Deprecated
diff --git a/annotation-file-utilities/tests/DuplicateAnnotation.java b/annotation-file-utilities/tests/DuplicateAnnotation.java new file mode 100644 index 0000000..f6e1f7d --- /dev/null +++ b/annotation-file-utilities/tests/DuplicateAnnotation.java
@@ -0,0 +1,30 @@ +package annotator.tests; + +// This test ensures that no annotation is re-inserted, if it already +// existed in the original source code. +public class DuplicateAnnotation { + + @SuppressWarnings("A") + void m1() { } + + @java.lang.SuppressWarnings("B") + void m2() { } + + /*@SuppressWarnings("C")*/ + void m3() { } + + /*@java.lang.SuppressWarnings("D")*/ + void m4() { } + + void m5() { } + + void m6() { + @SuppressWarnings("F") + Object o = new Object(); + } + + int /*@annotator.tests.TA*/ [] i; +} + +@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) +@interface TA {}
diff --git a/annotation-file-utilities/tests/EnumAnnotationParameter.goal b/annotation-file-utilities/tests/EnumAnnotationParameter.goal new file mode 100644 index 0000000..1b0a1e6 --- /dev/null +++ b/annotation-file-utilities/tests/EnumAnnotationParameter.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class EnumAnnotationParameter { + @java.lang.TestAnnotation(java.lang.TestEnum.A) + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumAnnotationParameter.jaif b/annotation-file-utilities/tests/EnumAnnotationParameter.jaif new file mode 100644 index 0000000..ef626da --- /dev/null +++ b/annotation-file-utilities/tests/EnumAnnotationParameter.jaif
@@ -0,0 +1,9 @@ +package java.lang: + +annotation @TestAnnotation: + enum java.lang.TestEnum value + +package annotator.tests: +class EnumAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value=A) \ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumAnnotationParameter.java b/annotation-file-utilities/tests/EnumAnnotationParameter.java new file mode 100644 index 0000000..58498e7 --- /dev/null +++ b/annotation-file-utilities/tests/EnumAnnotationParameter.java
@@ -0,0 +1,6 @@ +package annotator.tests; + +public class EnumAnnotationParameter { + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumListAnnotationParameter.goal b/annotation-file-utilities/tests/EnumListAnnotationParameter.goal new file mode 100644 index 0000000..d962d41 --- /dev/null +++ b/annotation-file-utilities/tests/EnumListAnnotationParameter.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class EnumListAnnotationParameter { + @java.lang.TestAnnotation({java.lang.TestEnum.A,java.lang.TestEnum.B}) + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumListAnnotationParameter.jaif b/annotation-file-utilities/tests/EnumListAnnotationParameter.jaif new file mode 100644 index 0000000..238fba2 --- /dev/null +++ b/annotation-file-utilities/tests/EnumListAnnotationParameter.jaif
@@ -0,0 +1,9 @@ +package java.lang: + +annotation @TestAnnotation: + enum java.lang.TestEnum[] value + +package annotator.tests: +class EnumListAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value={A,B})
diff --git a/annotation-file-utilities/tests/EnumListAnnotationParameter.java b/annotation-file-utilities/tests/EnumListAnnotationParameter.java new file mode 100644 index 0000000..aa1345f --- /dev/null +++ b/annotation-file-utilities/tests/EnumListAnnotationParameter.java
@@ -0,0 +1,6 @@ +package annotator.tests; + +public class EnumListAnnotationParameter { + public void foo() { + } +} \ No newline at end of file
diff --git a/annotation-file-utilities/tests/ExtImpl.goal b/annotation-file-utilities/tests/ExtImpl.goal new file mode 100644 index 0000000..9a01f5a --- /dev/null +++ b/annotation-file-utilities/tests/ExtImpl.goal
@@ -0,0 +1,57 @@ +package annotator.tests; + +public class ExtImpl { + class Top<X, Y> {} + interface Iface<A, B> {} + interface Iface2<C, D> {} + interface Iface3 {} + interface Iface4<T, @org.checkerframework.checker.nullness.qual.NonNull S extends @org.checkerframework.checker.nullness.qual.NonNull @org.checkerframework.checker.nullness.qual.UnknownKeyFor Iface4<T, S>> {} + + class C1 extends @A Top<@B Object, @C String> implements @D Iface<@E Integer, @F String> {} + + class C2 implements @A Iface<@B String, @C Object>, @D Iface2<@E Object, @F Float> {} + + class C3 { + class Iface3 implements annotator.tests.ExtImpl.Iface3 {} + + /* + * the jaif file says that the simple name of + * the return type in JVM format is + * LIface3; + */ + @java.lang.SuppressWarnings({}) + annotator.tests.ExtImpl.C3.Iface3 getI1() { + return null; + } + + /* + * in this case, the jaif file uses the fully qualified name + * for the return type + * Lannotator.tests.ExtImpl.C3.Iface3; + */ + @java.lang.SuppressWarnings({}) + annotator.tests.ExtImpl.C3.Iface3 getI2() { + return null; + } + + /* + * the jaif file uses the simple name of the return type + * LC3$Iface3; + */ + @java.lang.SuppressWarnings({}) + Iface3 getI3() { + return null; + } + + /* + * in the jaif file, the return type is Iface3 + * (ambiguous: could be short for the interface + * annotator.tests.ExtImpl.Iface3) + */ + @java.lang.SuppressWarnings({}) + Iface3 getI4() { + return null; + } + } +} +
diff --git a/annotation-file-utilities/tests/ExtImpl.jaif b/annotation-file-utilities/tests/ExtImpl.jaif new file mode 100644 index 0000000..a925aba --- /dev/null +++ b/annotation-file-utilities/tests/ExtImpl.jaif
@@ -0,0 +1,48 @@ +package org.checkerframework.checker.nullness.qual: + +annotation @NonNull: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME) + +annotation @UnknownKeyFor: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME) + +package: + +annotation @A: +annotation @B: +annotation @C: +annotation @D: +annotation @E: +annotation @F: + +package java.lang: + +annotation @SuppressWarnings: + String[] value + +package annotator.tests: + +class ExtImpl$C1: +extends: @A +inner-type 3, 0: @B +inner-type 3, 1: @C +implements 0: @D +inner-type 3, 0: @E +inner-type 3, 1: @F + +class ExtImpl$C2: +implements 0: @A +inner-type 3, 0: @B +inner-type 3, 1: @C +implements 1: @D +inner-type 3, 0: @E +inner-type 3, 1: @F + +class ExtImpl$C3: + method getI1()LIface3;: @SuppressWarnings({}) + method getI2()Lannotator.tests.ExtImpl$C3$Iface3;: @SuppressWarnings({}) + method getI3()LExtImpl$C3$Iface3;: @SuppressWarnings({}) + method getI4()LIface3;: @SuppressWarnings({}) + +class ExtImpl$Iface4: + typeparam 1: @org.checkerframework.checker.nullness.qual.NonNull + bound 1&1: @org.checkerframework.checker.nullness.qual.UnknownKeyFor @org.checkerframework.checker.nullness.qual.NonNull +
diff --git a/annotation-file-utilities/tests/ExtImpl.java b/annotation-file-utilities/tests/ExtImpl.java new file mode 100644 index 0000000..c612e2d --- /dev/null +++ b/annotation-file-utilities/tests/ExtImpl.java
@@ -0,0 +1,53 @@ +package annotator.tests; + +public class ExtImpl { + class Top<X, Y> {} + interface Iface<A, B> {} + interface Iface2<C, D> {} + interface Iface3 {} + interface Iface4<T, S extends Iface4<T, S>> {} + + class C1 extends Top<Object, String> implements Iface<Integer, String> {} + + class C2 implements Iface<String, Object>, Iface2<Object, Float> {} + + class C3 { + class Iface3 implements annotator.tests.ExtImpl.Iface3 {} + + /* + * the jaif file says that the simple name of + * the return type in JVM format is + * LIface3; + */ + annotator.tests.ExtImpl.C3.Iface3 getI1() { + return null; + } + + /* + * in this case, the jaif file uses the fully qualified name + * for the return type + * Lannotator.tests.ExtImpl.C3.Iface3; + */ + annotator.tests.ExtImpl.C3.Iface3 getI2() { + return null; + } + + /* + * the jaif file uses the simple name of the return type + * LC3$Iface3; + */ + Iface3 getI3() { + return null; + } + + /* + * in the jaif file, the return type is Iface3 + * (ambiguous: could be short for the interface + * annotator.tests.ExtImpl.Iface3) + */ + Iface3 getI4() { + return null; + } + } +} +
diff --git a/annotation-file-utilities/tests/FieldGenericArray.goal b/annotation-file-utilities/tests/FieldGenericArray.goal new file mode 100644 index 0000000..1a9f531 --- /dev/null +++ b/annotation-file-utilities/tests/FieldGenericArray.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.List; + +public class FieldGenericArray { + @InsideArray List<@GenericType Integer> @OuterMostType [] field; +}
diff --git a/annotation-file-utilities/tests/FieldGenericArray.jaif b/annotation-file-utilities/tests/FieldGenericArray.jaif new file mode 100644 index 0000000..7ea2624 --- /dev/null +++ b/annotation-file-utilities/tests/FieldGenericArray.jaif
@@ -0,0 +1,19 @@ +package : +annotation @OuterMostType: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @InsideArray: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @GenericType: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldGenericArray: + + field field: + type: @OuterMostType + inner-type 0, 0: @InsideArray + inner-type 0, 0, 3, 0: @GenericType + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/FieldGenericArray.java b/annotation-file-utilities/tests/FieldGenericArray.java new file mode 100644 index 0000000..5391ce9 --- /dev/null +++ b/annotation-file-utilities/tests/FieldGenericArray.java
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.List; + +public class FieldGenericArray { + List<Integer>[] field; +}
diff --git a/annotation-file-utilities/tests/FieldMultiple.goal b/annotation-file-utilities/tests/FieldMultiple.goal new file mode 100644 index 0000000..8869abc --- /dev/null +++ b/annotation-file-utilities/tests/FieldMultiple.goal
@@ -0,0 +1,12 @@ +package annotator.tests; + +@skip-test + +public class FieldMultiple { + public @java.lang.Tainted Integer foo; + public @java.lang.Tainted Integer bar; + @java.lang.Tainted + public Integer i; + @java.lang.Tainted + public Integer d; +}
diff --git a/annotation-file-utilities/tests/FieldMultiple.jaif b/annotation-file-utilities/tests/FieldMultiple.jaif new file mode 100644 index 0000000..3706c60 --- /dev/null +++ b/annotation-file-utilities/tests/FieldMultiple.jaif
@@ -0,0 +1,18 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldMultiple: + + field foo: + type: @java.lang.Tainted + + field bar: + type: @java.lang.Tainted + + field i: @java.lang.Tainted + + field d: @java.lang.Tainted + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/FieldMultiple.java b/annotation-file-utilities/tests/FieldMultiple.java new file mode 100644 index 0000000..3b75510 --- /dev/null +++ b/annotation-file-utilities/tests/FieldMultiple.java
@@ -0,0 +1,6 @@ +package annotator.tests; + +public class FieldMultiple { + public Integer foo, bar; + public Integer i, d; +}
diff --git a/annotation-file-utilities/tests/FieldSimple.goal b/annotation-file-utilities/tests/FieldSimple.goal new file mode 100644 index 0000000..5e8f1fd --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimple.goal
@@ -0,0 +1,5 @@ +package annotator.tests; + +public class FieldSimple { + private @java.lang.UnderInitialization Integer field; +}
diff --git a/annotation-file-utilities/tests/FieldSimple.jaif b/annotation-file-utilities/tests/FieldSimple.jaif new file mode 100644 index 0000000..aaf0872 --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimple.jaif
@@ -0,0 +1,11 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldSimple: + + field field: + type: @java.lang.UnderInitialization + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/FieldSimple.java b/annotation-file-utilities/tests/FieldSimple.java new file mode 100644 index 0000000..656e066 --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimple.java
@@ -0,0 +1,5 @@ +package annotator.tests; + +public class FieldSimple { + private Integer field; +}
diff --git a/annotation-file-utilities/tests/FieldSimpleArray.goal b/annotation-file-utilities/tests/FieldSimpleArray.goal new file mode 100644 index 0000000..1b299ca --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimpleArray.goal
@@ -0,0 +1,5 @@ +package annotator.tests; + +public class FieldSimpleArray { + @java.lang.UnderInitialization Integer @java.lang.Tainted [] field; +}
diff --git a/annotation-file-utilities/tests/FieldSimpleArray.jaif b/annotation-file-utilities/tests/FieldSimpleArray.jaif new file mode 100644 index 0000000..d7ad5a5 --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimpleArray.jaif
@@ -0,0 +1,15 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldSimpleArray: + + field field: + type: @java.lang.Tainted + inner-type 0, 0: @java.lang.UnderInitialization + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/FieldSimpleArray.java b/annotation-file-utilities/tests/FieldSimpleArray.java new file mode 100644 index 0000000..1c18d78 --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimpleArray.java
@@ -0,0 +1,5 @@ +package annotator.tests; + +public class FieldSimpleArray { + Integer[] field; +}
diff --git a/annotation-file-utilities/tests/FieldSimpleGeneric.goal b/annotation-file-utilities/tests/FieldSimpleGeneric.goal new file mode 100644 index 0000000..71270eb --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimpleGeneric.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.List; + +public class FieldSimpleGeneric { + @java.lang.UnderInitialization List<@java.lang.Tainted Integer> field; +}
diff --git a/annotation-file-utilities/tests/FieldSimpleGeneric.jaif b/annotation-file-utilities/tests/FieldSimpleGeneric.jaif new file mode 100644 index 0000000..6271030 --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimpleGeneric.jaif
@@ -0,0 +1,15 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldSimpleGeneric: + + field field: + type: @java.lang.UnderInitialization + inner-type 3, 0: @java.lang.Tainted + + method <init>()V: +
diff --git a/annotation-file-utilities/tests/FieldSimpleGeneric.java b/annotation-file-utilities/tests/FieldSimpleGeneric.java new file mode 100644 index 0000000..257b91e --- /dev/null +++ b/annotation-file-utilities/tests/FieldSimpleGeneric.java
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.List; + +public class FieldSimpleGeneric { + List<Integer> field; +}
diff --git a/annotation-file-utilities/tests/GenericAnnoBound.goal b/annotation-file-utilities/tests/GenericAnnoBound.goal new file mode 100644 index 0000000..733b058 --- /dev/null +++ b/annotation-file-utilities/tests/GenericAnnoBound.goal
@@ -0,0 +1,9 @@ +import java.lang.annotation.*; + +@Target(ElementType.TYPE_USE) +@interface Bla {} + +public class GenericAnnoBound<X extends @Bla Object> { + GenericAnnoBound(@Bla GenericAnnoBound<X> n, X p) { + } +}
diff --git a/annotation-file-utilities/tests/GenericAnnoBound.jaif b/annotation-file-utilities/tests/GenericAnnoBound.jaif new file mode 100644 index 0000000..d228304 --- /dev/null +++ b/annotation-file-utilities/tests/GenericAnnoBound.jaif
@@ -0,0 +1,8 @@ +package: +annotation @Bla: + +class GenericAnnoBound: +method <init>(LGenericAnnoBound;Ljava/lang/Object;)V: +parameter 0: +type: @Bla +
diff --git a/annotation-file-utilities/tests/GenericAnnoBound.java b/annotation-file-utilities/tests/GenericAnnoBound.java new file mode 100644 index 0000000..a5c6ee4 --- /dev/null +++ b/annotation-file-utilities/tests/GenericAnnoBound.java
@@ -0,0 +1,9 @@ +import java.lang.annotation.*; + +@Target(ElementType.TYPE_USE) +@interface Bla {} + +public class GenericAnnoBound<X extends @Bla Object> { + GenericAnnoBound(GenericAnnoBound<X> n, X p) { + } +}
diff --git a/annotation-file-utilities/tests/GenericArg.goal b/annotation-file-utilities/tests/GenericArg.goal new file mode 100644 index 0000000..15e5255 --- /dev/null +++ b/annotation-file-utilities/tests/GenericArg.goal
@@ -0,0 +1,31 @@ +public class GenericArg<X> { + + void mp(X p) { + @X + Object l; + } + + X mr() { + @X + Object r; + return null; + } + + <Y extends Number> void foo(Y p) { + @X + Object k; + } + + <Z extends Integer> Z bar() { + @X + Integer j; + return null; + } + + class Tricky { + void argh(X p) { + @X + Object a; + } + } +}
diff --git a/annotation-file-utilities/tests/GenericArg.jaif b/annotation-file-utilities/tests/GenericArg.jaif new file mode 100644 index 0000000..9e597be --- /dev/null +++ b/annotation-file-utilities/tests/GenericArg.jaif
@@ -0,0 +1,20 @@ +package: +annotation @X: + +class GenericArg: +method mp(Ljava/lang/Object;)V: +local l: @X + +method mr()Ljava/lang/Object;: +local r: @X + +method foo(Ljava/lang/Number;)V: +local k: @X + +method bar()Ljava/lang/Integer;: +local j: @X + +class GenericArg$Tricky: +method argh(Ljava/lang/Object;)V: +local a: @X +
diff --git a/annotation-file-utilities/tests/GenericArg.java b/annotation-file-utilities/tests/GenericArg.java new file mode 100644 index 0000000..82806a0 --- /dev/null +++ b/annotation-file-utilities/tests/GenericArg.java
@@ -0,0 +1,26 @@ +public class GenericArg<X> { + + void mp(X p) { + Object l; + } + + X mr() { + Object r; + return null; + } + + <Y extends Number> void foo(Y p) { + Object k; + } + + <Z extends Integer> Z bar() { + Integer j; + return null; + } + + class Tricky { + void argh(X p) { + Object a; + } + } +}
diff --git a/annotation-file-utilities/tests/GenericCell.goal b/annotation-file-utilities/tests/GenericCell.goal new file mode 100644 index 0000000..3ce9212 --- /dev/null +++ b/annotation-file-utilities/tests/GenericCell.goal
@@ -0,0 +1,32 @@ +package annotator.tests; + +import java.util.List; + +public class GenericCell { + private @java.lang.ThisUnderInitialization List<@java.lang.ThisUnderInitialization IntCell> internalList; + + public GenericCell(@java.lang.UnderInitialization List<@java.lang.UnderInitialization IntCell> list) { + internalList = list; + } + + public @java.lang.PolyRaw List<@java.lang.PolyRaw IntCell> getList(@java.lang.PolyRaw GenericCell this) { + return internalList; + } + + public static class IntCell { + private int i; + + public IntCell(int in) { + this.i = in; + } + + public void set(int in) { + this.i = in; + } + + public int get() { + return i; + } + } + +}
diff --git a/annotation-file-utilities/tests/GenericCell.jaif b/annotation-file-utilities/tests/GenericCell.jaif new file mode 100644 index 0000000..60d7e94 --- /dev/null +++ b/annotation-file-utilities/tests/GenericCell.jaif
@@ -0,0 +1,26 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @PolyRaw: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @ThisUnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class GenericCell: + + field internalList: + type: @java.lang.ThisUnderInitialization + inner-type 3, 0: @java.lang.ThisUnderInitialization + + method <init>(Ljava/util/List;)V: + parameter #0: + type: @java.lang.UnderInitialization + inner-type 3, 0: @java.lang.UnderInitialization + + method getList()Ljava/util/List;: + return: @java.lang.PolyRaw + inner-type 3, 0: @java.lang.PolyRaw + receiver: @java.lang.PolyRaw +
diff --git a/annotation-file-utilities/tests/GenericCell.java b/annotation-file-utilities/tests/GenericCell.java new file mode 100644 index 0000000..688d472 --- /dev/null +++ b/annotation-file-utilities/tests/GenericCell.java
@@ -0,0 +1,32 @@ +package annotator.tests; + +import java.util.List; + +public class GenericCell { + private List<IntCell> internalList; + + public GenericCell(List<IntCell> list) { + internalList = list; + } + + public List<IntCell> getList() { + return internalList; + } + + public static class IntCell { + private int i; + + public IntCell(int in) { + this.i = in; + } + + public void set(int in) { + this.i = in; + } + + public int get() { + return i; + } + } + +}
diff --git a/annotation-file-utilities/tests/GenericCellDoubled.goal b/annotation-file-utilities/tests/GenericCellDoubled.goal new file mode 100644 index 0000000..2790cf8 --- /dev/null +++ b/annotation-file-utilities/tests/GenericCellDoubled.goal
@@ -0,0 +1,20 @@ +import java.lang.annotation.*; +import java.util.*; + +@Target(ElementType.TYPE_USE) +@interface X {} +@Target(ElementType.TYPE_USE) +@interface Y {} + +public class GenericCellDoubled { + List<@X Object> f; + @X List<@X Object> g; + @X List<@X ArrayList<@X Object>> h; + @X List<@X ArrayList<@X Object>> i; + + Map<List<Object>, ArrayList<@X Integer>> j1; + @X Map<List<@X Object>, @X ArrayList<@X Integer>> j2; + @X Map<@X List<@X Object>, @X @Y ArrayList<@X Integer>> j3; + + @X @Y List k; +}
diff --git a/annotation-file-utilities/tests/GenericCellDoubled.jaif b/annotation-file-utilities/tests/GenericCellDoubled.jaif new file mode 100644 index 0000000..4d67ac9 --- /dev/null +++ b/annotation-file-utilities/tests/GenericCellDoubled.jaif
@@ -0,0 +1,47 @@ +package: +annotation @X: +annotation @Y: + +package: +class GenericCellDoubled: + +field f: +type: +inner-type 3, 0: @X + +field g: +type: +inner-type 3, 0: @X + +field h: +type: +inner-type 3, 0, 3, 0: @X + +field i: +type: +inner-type 3, 0: @X + +field j1: +type: +inner-type 3, 1, 3, 0: @X + +field j2: +type: +inner-type 3, 1, 3, 0: @X + +field j3: +type: +inner-type 3, 1, 3, 0: @X + +// duplicate, @X already there -> no effect +field j3: +type: +inner-type 3, 1: @X + +// real +field j3: +type: +inner-type 3, 1: @Y + +field k: +type: @Y \ No newline at end of file
diff --git a/annotation-file-utilities/tests/GenericCellDoubled.java b/annotation-file-utilities/tests/GenericCellDoubled.java new file mode 100644 index 0000000..3dc9684 --- /dev/null +++ b/annotation-file-utilities/tests/GenericCellDoubled.java
@@ -0,0 +1,20 @@ +import java.lang.annotation.*; +import java.util.*; + +@Target(ElementType.TYPE_USE) +@interface X {} +@Target(ElementType.TYPE_USE) +@interface Y {} + +public class GenericCellDoubled { + List<Object> f; + @X List<Object> g; + @X List<@X ArrayList<Object>> h; + @X List<ArrayList<@X Object>> i; + + Map<List<Object>, ArrayList<Integer>> j1; + @X Map<List<@X Object>, @X ArrayList<Integer>> j2; + @X Map<@X List<@X Object>, @X ArrayList<Integer>> j3; + + @X List k; +}
diff --git a/annotation-file-utilities/tests/GenericMultiLevel.goal b/annotation-file-utilities/tests/GenericMultiLevel.goal new file mode 100644 index 0000000..dfb5059 --- /dev/null +++ b/annotation-file-utilities/tests/GenericMultiLevel.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.*; + +public class GenericMultiLevel { + @java.lang.A Map<@java.lang.B Comparable<@java.lang.C Object>, @java.lang.G List<@java.lang.H Date>> field; +}
diff --git a/annotation-file-utilities/tests/GenericMultiLevel.jaif b/annotation-file-utilities/tests/GenericMultiLevel.jaif new file mode 100644 index 0000000..5bef1b2 --- /dev/null +++ b/annotation-file-utilities/tests/GenericMultiLevel.jaif
@@ -0,0 +1,19 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @H: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class GenericMultiLevel: + + field field: + type: @java.lang.A + inner-type 3, 0: @B + inner-type 3, 0, 3, 0: @C + inner-type 3, 1: @G + inner-type 3, 1, 3, 0: @H
diff --git a/annotation-file-utilities/tests/GenericMultiLevel.java b/annotation-file-utilities/tests/GenericMultiLevel.java new file mode 100644 index 0000000..c881b4b --- /dev/null +++ b/annotation-file-utilities/tests/GenericMultiLevel.java
@@ -0,0 +1,7 @@ +package annotator.tests; + +import java.util.*; + +public class GenericMultiLevel { + Map<Comparable<Object>, List<Date>> field; +}
diff --git a/annotation-file-utilities/tests/ImplicitUpper.goal b/annotation-file-utilities/tests/ImplicitUpper.goal new file mode 100644 index 0000000..657ff49 --- /dev/null +++ b/annotation-file-utilities/tests/ImplicitUpper.goal
@@ -0,0 +1,8 @@ +class ExplicitUpper<X extends @A Object> extends @B Object { +} + +public class ImplicitUpper<Y extends @A Object> extends @B Object { +} + +class Monomorphic extends @B Object { +}
diff --git a/annotation-file-utilities/tests/ImplicitUpper.jaif b/annotation-file-utilities/tests/ImplicitUpper.jaif new file mode 100644 index 0000000..2c057e5 --- /dev/null +++ b/annotation-file-utilities/tests/ImplicitUpper.jaif
@@ -0,0 +1,16 @@ +package: +annotation @A: +package: +annotation @B: + +class ExplicitUpper: +bound 0 & 0: @A +extends: @B + +class ImplicitUpper: +bound 0 & 0: @A +extends: @B + +class Monomorphic: + insert-annotation Class.bound -1: @B +
diff --git a/annotation-file-utilities/tests/ImplicitUpper.java b/annotation-file-utilities/tests/ImplicitUpper.java new file mode 100644 index 0000000..79e55b5 --- /dev/null +++ b/annotation-file-utilities/tests/ImplicitUpper.java
@@ -0,0 +1,8 @@ +class ExplicitUpper<X extends Object> extends Object { +} + +public class ImplicitUpper<Y> { +} + +class Monomorphic { +}
diff --git a/annotation-file-utilities/tests/Initializers.goal b/annotation-file-utilities/tests/Initializers.goal new file mode 100644 index 0000000..c9ddfd1 --- /dev/null +++ b/annotation-file-utilities/tests/Initializers.goal
@@ -0,0 +1,31 @@ +package annotator.tests; + +public class Initializers { + static { + String s1 = new @X String(); + } + + static { + String s2 = new @X String(); + } + + { + Object o1 = new @X Object(); + } + + { + Object o2 = new @X Object(); + } + + enum MyEnum { + A; + + static { + String s = new @X String(); + } + + { + Object o = new @X Object(); + } + } +}
diff --git a/annotation-file-utilities/tests/Initializers.jaif b/annotation-file-utilities/tests/Initializers.jaif new file mode 100644 index 0000000..1488dc8 --- /dev/null +++ b/annotation-file-utilities/tests/Initializers.jaif
@@ -0,0 +1,29 @@ +package : +annotation @X: + +package annotator.tests: + +class Initializers: +staticinit *0: +new *0: @X + +class Initializers: +staticinit *1: +new *0: @X + +class Initializers: +instanceinit *0: +new *0: @X + +class Initializers: +instanceinit *1: +new *0: @X + +class Initializers$MyEnum: +staticinit *0: +new *0: @X + +class Initializers$MyEnum: +instanceinit *0: +new *0: @X +
diff --git a/annotation-file-utilities/tests/Initializers.java b/annotation-file-utilities/tests/Initializers.java new file mode 100644 index 0000000..9c712cc --- /dev/null +++ b/annotation-file-utilities/tests/Initializers.java
@@ -0,0 +1,31 @@ +package annotator.tests; + +public class Initializers { + static { + String s1 = new String(); + } + + static { + String s2 = new String(); + } + + { + Object o1 = new Object(); + } + + { + Object o2 = new Object(); + } + + enum MyEnum { + A; + + static { + String s = new String(); + } + + { + Object o = new Object(); + } + } +}
diff --git a/annotation-file-utilities/tests/InnerClass.goal b/annotation-file-utilities/tests/InnerClass.goal new file mode 100644 index 0000000..2caf521 --- /dev/null +++ b/annotation-file-utilities/tests/InnerClass.goal
@@ -0,0 +1,34 @@ +public class InnerClass { + + void m() { + @A(1) Object o = new @A(2) Object(); + if (o instanceof @A(3) String) { + @A(4) String s = (@A(5) String) o; + } + + class Inner { + + void m() { + @B(1) Object o = new @B(2) Object(); + if (o instanceof @B(3) String) { + @B(4) String s = (@B(5) String) o; + } + } + } + + new InnerClass() { + + void m() { + @C(1) Object o = new @C(2) Object(); + if (o instanceof @C(3) String) { + @C(4) String s = (@C(5) String) o; + } + } + }; + + o = new @D(2) Object(); + if (o instanceof @D(3) String) { + @D(4) String s = (@D(5) String) o; + } + } +}
diff --git a/annotation-file-utilities/tests/InnerClass.jaif b/annotation-file-utilities/tests/InnerClass.jaif new file mode 100644 index 0000000..1c96781 --- /dev/null +++ b/annotation-file-utilities/tests/InnerClass.jaif
@@ -0,0 +1,37 @@ +package: + annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + int value + annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) + int value + annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) + int value + annotation @D: @java.lang.annotation.Target(value={TYPE_USE}) + int value + + class InnerClass: + method m()V: + local o: @A(1) + new *0: @A(2) + instanceof *0: @A(3) + local s *0: @A(4) + typecast *0: @A(5) + new *2: @D(2) + instanceof *1: @D(3) + local s *1: @D(4) + typecast *1: @D(5) + + class InnerClass$1Inner: + method m()V: + local o: @B(1) + new *0: @B(2) + instanceof *0: @B(3) + local s *0: @B(4) + typecast *0: @B(5) + + class InnerClass$1: + method m()V: + local o: @C(1) + new *0: @C(2) + instanceof *0: @C(3) + local s *0: @C(4) + typecast *0: @C(5)
diff --git a/annotation-file-utilities/tests/InnerClass.java b/annotation-file-utilities/tests/InnerClass.java new file mode 100644 index 0000000..035f072 --- /dev/null +++ b/annotation-file-utilities/tests/InnerClass.java
@@ -0,0 +1,34 @@ +public class InnerClass { + + void m() { + Object o = new Object(); + if (o instanceof String) { + String s = (String) o; + } + + class Inner { + + void m() { + Object o = new Object(); + if (o instanceof String) { + String s = (String) o; + } + } + } + + new InnerClass() { + + void m() { + Object o = new Object(); + if (o instanceof String) { + String s = (String) o; + } + } + }; + + o = new Object(); + if (o instanceof String) { + String s = (String) o; + } + } +}
diff --git a/annotation-file-utilities/tests/InnerClassAnonymous.goal b/annotation-file-utilities/tests/InnerClassAnonymous.goal new file mode 100644 index 0000000..84416b3 --- /dev/null +++ b/annotation-file-utilities/tests/InnerClassAnonymous.goal
@@ -0,0 +1,30 @@ +package annotator.tests; + +import java.io.Serializable; + +public class InnerClassAnonymous { + public Object field; + + public class NamedInnerClass { + public Object namedField; + } + + public Serializable foo() { + return new Serializable() { + public final @java.lang.Tainted Object serialVersionUID = null; + }; + } + + public Serializable bar() { + return new Serializable() { + private static final @NonNegative long serialVersionUID = 0; + }; + } + + public Serializable baz() { + return new Serializable() { + private static final @InnerlyAnnotated long serialVersionUID = 0; + }; + } + +}
diff --git a/annotation-file-utilities/tests/InnerClassAnonymous.jaif b/annotation-file-utilities/tests/InnerClassAnonymous.jaif new file mode 100644 index 0000000..6c752a2 --- /dev/null +++ b/annotation-file-utilities/tests/InnerClassAnonymous.jaif
@@ -0,0 +1,23 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package: +annotation @NonNegative: @java.lang.annotation.Target(value={TYPE_USE}) +annotation @InnerlyAnnotated: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InnerClassAnonymous$1: + + field serialVersionUID: + type: @java.lang.Tainted + +class InnerClassAnonymous$2: + + field serialVersionUID: + type: @NonNegative + +class InnerClassAnonymous$3: + + field serialVersionUID: + type: @InnerlyAnnotated +
diff --git a/annotation-file-utilities/tests/InnerClassAnonymous.java b/annotation-file-utilities/tests/InnerClassAnonymous.java new file mode 100644 index 0000000..335e21d --- /dev/null +++ b/annotation-file-utilities/tests/InnerClassAnonymous.java
@@ -0,0 +1,30 @@ +package annotator.tests; + +import java.io.Serializable; + +public class InnerClassAnonymous { + public Object field; + + public class NamedInnerClass { + public Object namedField; + } + + public Serializable foo() { + return new Serializable() { + public final Object serialVersionUID = null; + }; + } + + public Serializable bar() { + return new Serializable() { + private static final long serialVersionUID = 0; + }; + } + + public Serializable baz() { + return new Serializable() { + private static final long serialVersionUID = 0; + }; + } + +}
diff --git a/annotation-file-utilities/tests/InnerClassSimple.goal b/annotation-file-utilities/tests/InnerClassSimple.goal new file mode 100644 index 0000000..3ebb0ff --- /dev/null +++ b/annotation-file-utilities/tests/InnerClassSimple.goal
@@ -0,0 +1,12 @@ +package annotator.tests; + +import java.util.Date; + +@skip-test +public class InnerClassSimple { + public Integer field; + + @java.lang.Tainted @java.lang.DoesNotExist public class ActualInnerClass { + @java.lang.Tainted Date d; + } +}
diff --git a/annotation-file-utilities/tests/InnerClassSimple.jaif b/annotation-file-utilities/tests/InnerClassSimple.jaif new file mode 100644 index 0000000..5a126cd --- /dev/null +++ b/annotation-file-utilities/tests/InnerClassSimple.jaif
@@ -0,0 +1,13 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @DoesNotExist: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InnerClassSimple$ActualInnerClass: @java.lang.Tainted + + field d: + type: @java.lang.Tainted + +package annotator.tests: +class InnerClassSimple$ActualInnerClass: @java.lang.DoesNotExist +
diff --git a/annotation-file-utilities/tests/InnerClassSimple.java b/annotation-file-utilities/tests/InnerClassSimple.java new file mode 100644 index 0000000..7f13944 --- /dev/null +++ b/annotation-file-utilities/tests/InnerClassSimple.java
@@ -0,0 +1,11 @@ +package annotator.tests; + +import java.util.Date; + +public class InnerClassSimple { + public Integer field; + + public class ActualInnerClass { + Date d; + } +}
diff --git a/annotation-file-utilities/tests/InnerReceivers.goal b/annotation-file-utilities/tests/InnerReceivers.goal new file mode 100644 index 0000000..33e278e --- /dev/null +++ b/annotation-file-utilities/tests/InnerReceivers.goal
@@ -0,0 +1,71 @@ +package annotator.tests; + +public class InnerReceivers { + + InnerReceivers i = new InnerReceivers() { + + void m() {} + + class Inner { + void m(@A Inner this) {} + + void m1(@B Inner this) {} + } + }; + + void m(@C InnerReceivers this) {} + + void m1(@D InnerReceivers this) {} + + void m2(annotator.tests.@D1 InnerReceivers this) {} + + class Inner1<Y, Z> { + + void m(@E(0) InnerReceivers.@E(1) Inner1<@E(2) Y, @E(3) Z> this) {} + + void m1(@F InnerReceivers.Inner1<Y, Z> this) {} + + void m2(annotator.tests.@F1 InnerReceivers.Inner1<Y, Z> this) {} + + class Inner2 { + + void m(@G(0) InnerReceivers.@G(1) Inner1<@G(2) Y, @G(3) Z>.@G(4) Inner2 this) {} + + void m1(@H InnerReceivers.Inner1<Y, Z>.Inner2 this) {} + } + } + + static class StaticInner1 { + + void m(InnerReceivers.@I StaticInner1 this) {} + + void m1(InnerReceivers.@J StaticInner1 this) {} + + void m2(annotator.tests.InnerReceivers.@K StaticInner1 this) {} + } + + static class StaticInner3<Y, Z> { + + void m(InnerReceivers.@I1(0) StaticInner3<@I1(1) Y, @I1(2) Z> this) {} + + void m1(InnerReceivers.@J StaticInner3<Y, Z> this) {} + + void m2(annotator.tests.InnerReceivers.@K StaticInner3<Y, Z> this) {} + } +} + +class Outer<K> { + static class StaticInner2 { + + void m(Outer.@L StaticInner2 this) {} + + void m1(Outer.@M StaticInner2 this) {} + + void m2(annotator.tests.Outer.@N StaticInner2 this) {} + + static class StaticInner3 { + + void m(Outer.StaticInner2.@O StaticInner3 this) {} + } + } +}
diff --git a/annotation-file-utilities/tests/InnerReceivers.jaif b/annotation-file-utilities/tests/InnerReceivers.jaif new file mode 100644 index 0000000..b15b53d --- /dev/null +++ b/annotation-file-utilities/tests/InnerReceivers.jaif
@@ -0,0 +1,108 @@ +package: + annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @D: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @D1: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @E: @java.lang.annotation.Target(value={TYPE_USE}) + int value + annotation @F: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @F1: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @G: @java.lang.annotation.Target(value={TYPE_USE}) + int value + annotation @H: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @I: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @I1: @java.lang.annotation.Target(value={TYPE_USE}) + int value + annotation @J: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @K: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @L: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @M: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @N: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @O: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @P: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @X: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: + // Try to insert a receiver parameter on a method declaration in an + // anonymous inner class. This is illegal, so should do nothing. + class InnerReceivers$1: + method m()V: + receiver: @X + + class InnerReceivers$1$Inner: + method m()V: + receiver: @A + method m1()V: + receiver: @B + + class InnerReceivers: + method m()V: + receiver: @C + method m1()V: + receiver: @D + method m2()V: + receiver: @D1 + + class InnerReceivers$Inner1: + method m()V: + receiver: @E(0) + inner-type 1, 0: @E(1) + inner-type 1, 0, 3, 0: @E(2) + inner-type 1, 0, 3, 1: @E(3) + // The following two annotations are in incorrect locations and + // should not be inserted. + inner-type 3, 0: @X + inner-type 3, 1: @X + method m1()V: + receiver: @F + method m2()V: + receiver: @F1 + + class InnerReceivers$Inner1$Inner2: + method m()V: + receiver: @G(0) + inner-type 1, 0: @G(1) + inner-type 1, 0, 3, 0: @G(2) + inner-type 1, 0, 3, 1: @G(3) + inner-type 1, 0, 1, 0: @G(4) + method m1()V: + receiver: @H + + class InnerReceivers$StaticInner1: + method m()V: + receiver: @I + // Incorrect location, should not be inserted + inner-type 1, 0: @X + method m1()V: + receiver: @J + method m2()V: + receiver: @K + + class InnerReceivers$StaticInner3: + method m()V: + receiver: @I1(0) + inner-type 3, 0: @I1(1) + inner-type 3, 1: @I1(2) + // Incorrect location, should not be inserted + inner-type 1, 0: @X + method m1()V: + receiver: @J + method m2()V: + receiver: @K + + class Outer$StaticInner2: + method m()V: + receiver: @L + // Incorrect location, should not be inserted + inner-type 1, 0: @X + method m1()V: + receiver: @M + method m2()V: + receiver: @N + + class Outer$StaticInner2$StaticInner3: + method m()V: + receiver: @O + // Incorrect location, should not be inserted + inner-type 1, 0: @X
diff --git a/annotation-file-utilities/tests/InnerReceivers.java b/annotation-file-utilities/tests/InnerReceivers.java new file mode 100644 index 0000000..d33134a --- /dev/null +++ b/annotation-file-utilities/tests/InnerReceivers.java
@@ -0,0 +1,71 @@ +package annotator.tests; + +public class InnerReceivers { + + InnerReceivers i = new InnerReceivers() { + + void m() {} + + class Inner { + void m() {} + + void m1(Inner this) {} + } + }; + + void m() {} + + void m1(InnerReceivers this) {} + + void m2(annotator.tests.InnerReceivers this) {} + + class Inner1<Y, Z> { + + void m() {} + + void m1(InnerReceivers.Inner1<Y, Z> this) {} + + void m2(annotator.tests.InnerReceivers.Inner1<Y, Z> this) {} + + class Inner2 { + + void m() {} + + void m1(InnerReceivers.Inner1<Y, Z>.Inner2 this) {} + } + } + + static class StaticInner1 { + + void m() {} + + void m1(InnerReceivers.StaticInner1 this) {} + + void m2(annotator.tests.InnerReceivers.StaticInner1 this) {} + } + + static class StaticInner3<Y, Z> { + + void m() {} + + void m1(InnerReceivers.StaticInner3<Y, Z> this) {} + + void m2(annotator.tests.InnerReceivers.StaticInner3<Y, Z> this) {} + } +} + +class Outer<K> { + static class StaticInner2 { + + void m() {} + + void m1(Outer.StaticInner2 this) {} + + void m2(annotator.tests.Outer.StaticInner2 this) {} + + static class StaticInner3 { + + void m() {} + } + } +}
diff --git a/annotation-file-utilities/tests/InnerTypeResolution.goal b/annotation-file-utilities/tests/InnerTypeResolution.goal new file mode 100644 index 0000000..f1cbc0c --- /dev/null +++ b/annotation-file-utilities/tests/InnerTypeResolution.goal
@@ -0,0 +1,16 @@ +package annotator.tests; + +import java.util.Map; + +public class InnerTypeResolution { + @java.lang.annotation.Deprecated + Map.Entry method01(@java.lang.annotation.Tainted Map m) { + return null; + } + + @java.lang.annotation.Deprecated + Map.Entry method02(java.util.Map<@java.lang.annotation.Tainted String, Object> m) { + return null; + } +} +
diff --git a/annotation-file-utilities/tests/InnerTypeResolution.jaif b/annotation-file-utilities/tests/InnerTypeResolution.jaif new file mode 100644 index 0000000..54331ba --- /dev/null +++ b/annotation-file-utilities/tests/InnerTypeResolution.jaif
@@ -0,0 +1,21 @@ +package java.lang.annotation: +annotation @Deprecated: +annotation @Tainted: + +package annotator.tests: +class InnerTypeResolution: + + method <init>()V: + return: + + method method01(Ljava/util/Map;)Ljava/util/Map$Entry;: @Deprecated + return: + parameter #0: + type: @Tainted + + method method02(Ljava/util/Map;)Ljava/util/Map$Entry;: @Deprecated + return: + parameter #0: + type: + inner-type 3, 0: @Tainted +
diff --git a/annotation-file-utilities/tests/InnerTypeResolution.java b/annotation-file-utilities/tests/InnerTypeResolution.java new file mode 100644 index 0000000..e7fee87 --- /dev/null +++ b/annotation-file-utilities/tests/InnerTypeResolution.java
@@ -0,0 +1,14 @@ +package annotator.tests; + +import java.util.Map; + +public class InnerTypeResolution { + Map.Entry method01(Map m) { + return null; + } + + Map.Entry method02(java.util.Map<String, Object> m) { + return null; + } +} +
diff --git a/annotation-file-utilities/tests/InstanceOfMultiple.goal b/annotation-file-utilities/tests/InstanceOfMultiple.goal new file mode 100644 index 0000000..4be208d --- /dev/null +++ b/annotation-file-utilities/tests/InstanceOfMultiple.goal
@@ -0,0 +1,19 @@ +package annotator.tests; + +import java.util.List; + +public class InstanceOfMultiple { + public void foo(Object o) { + if (o instanceof @java.lang.Tainted List) { + if (o instanceof InstanceOfMultiple) { + if (o instanceof @java.lang.UnderInitialization Object) { + System.out.println(o); + } + } + } + + if (o instanceof @java.lang.UnderInitialization List<@java.lang.Tainted ?>) { + System.out.println(o); + } + } +}
diff --git a/annotation-file-utilities/tests/InstanceOfMultiple.jaif b/annotation-file-utilities/tests/InstanceOfMultiple.jaif new file mode 100644 index 0000000..328bbda --- /dev/null +++ b/annotation-file-utilities/tests/InstanceOfMultiple.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InstanceOfMultiple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + instanceof #1: @java.lang.Tainted + instanceof #15: @java.lang.UnderInitialization + instanceof #29: @java.lang.UnderInitialization + inner-type 3, 0: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/InstanceOfMultiple.java b/annotation-file-utilities/tests/InstanceOfMultiple.java new file mode 100644 index 0000000..9995ba7 --- /dev/null +++ b/annotation-file-utilities/tests/InstanceOfMultiple.java
@@ -0,0 +1,19 @@ +package annotator.tests; + +import java.util.List; + +public class InstanceOfMultiple { + public void foo(Object o) { + if (o instanceof List) { + if (o instanceof InstanceOfMultiple) { + if (o instanceof Object) { + System.out.println(o); + } + } + } + + if (o instanceof List<?>) { + System.out.println(o); + } + } +}
diff --git a/annotation-file-utilities/tests/InstanceOfSimple.goal b/annotation-file-utilities/tests/InstanceOfSimple.goal new file mode 100644 index 0000000..e891d46 --- /dev/null +++ b/annotation-file-utilities/tests/InstanceOfSimple.goal
@@ -0,0 +1,12 @@ +package annotator.tests; + +import java.util.List; + +public class InstanceOfSimple { + public void foo(Object o) { + if (o instanceof @java.lang.Tainted List) { + o = new Object(); + } + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/InstanceOfSimple.jaif b/annotation-file-utilities/tests/InstanceOfSimple.jaif new file mode 100644 index 0000000..02a2fc9 --- /dev/null +++ b/annotation-file-utilities/tests/InstanceOfSimple.jaif
@@ -0,0 +1,11 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InstanceOfSimple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + instanceof #1: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/InstanceOfSimple.java b/annotation-file-utilities/tests/InstanceOfSimple.java new file mode 100644 index 0000000..e55d8fa --- /dev/null +++ b/annotation-file-utilities/tests/InstanceOfSimple.java
@@ -0,0 +1,12 @@ +package annotator.tests; + +import java.util.List; + +public class InstanceOfSimple { + public void foo(Object o) { + if (o instanceof List) { + o = new Object(); + } + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/IntCell.goal b/annotation-file-utilities/tests/IntCell.goal new file mode 100644 index 0000000..8231c3f --- /dev/null +++ b/annotation-file-utilities/tests/IntCell.goal
@@ -0,0 +1,17 @@ +package annotator.tests; + +public class IntCell { + private int i; + + public IntCell(int in) { + this.i = in; + } + + public void set(@java.lang.UnderInitialization IntCell this, int in) { + this.i = in; + } + + public int get(@java.lang.Tainted IntCell this) { + return i; + } +}
diff --git a/annotation-file-utilities/tests/IntCell.jaif b/annotation-file-utilities/tests/IntCell.jaif new file mode 100644 index 0000000..9b00af7 --- /dev/null +++ b/annotation-file-utilities/tests/IntCell.jaif
@@ -0,0 +1,19 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class IntCell: + + field i: + + method <init>(I)V: + + method set(I)V: + receiver: @java.lang.UnderInitialization + + method get()I: + receiver: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/IntCell.java b/annotation-file-utilities/tests/IntCell.java new file mode 100644 index 0000000..ddc982b --- /dev/null +++ b/annotation-file-utilities/tests/IntCell.java
@@ -0,0 +1,17 @@ +package annotator.tests; + +public class IntCell { + private int i; + + public IntCell(int in) { + this.i = in; + } + + public void set(int in) { + this.i = in; + } + + public int get() { + return i; + } +}
diff --git a/annotation-file-utilities/tests/LambdaExpression.goal b/annotation-file-utilities/tests/LambdaExpression.goal new file mode 100644 index 0000000..373fe37 --- /dev/null +++ b/annotation-file-utilities/tests/LambdaExpression.goal
@@ -0,0 +1,64 @@ +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntBinaryOperator; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToIntBiFunction; + +@Target(ElementType.TYPE_USE) @interface A {} + +@Target(ElementType.TYPE_USE) @interface B {} + +@Target(ElementType.TYPE_USE) @interface C {} + +@skip-test +class LambdaExpression { + //Single inferred-type parameter + IntFunction<Integer> f0 = (x) -> x+1; + + //Parentheses optional for single inferred-type parameter + IntFunction<Integer> f1 = x -> x+1; + + //Single declared-type parameter, expression body + IntFunction<Integer> f2 = (@A int x) -> x+1; + + //Single declared-type parameter, block body + IntFunction<Integer> f3 = (@A int x) -> { return x+1; }; + + //Multiple declared-type parameters + IntBinaryOperator f4 = (@A int x, @B int y) -> x+y; + + //Generic argument type + static final ToIntBiFunction<String[], List<? extends CharSequence>> + selectCommon = (@A String @B [] array, + @A List<@B ? extends @C CharSequence> list) -> + { + int total = 0; + for (int i = 0; i < array.length; i++) { + Iterator<? extends CharSequence> iter = list.iterator(); + String str = array[i]; + while (iter.hasNext()) { + CharSequence seq = iter.next(); + if (seq.toString().equals(str)) { + ++total; + iter.remove(); + break; + } + } + } + return total; + }; + + public static void main(String[] args) { + String[] ss = {"a", "b"}; + System.out.println(selectCommon.applyAsInt(args, Arrays.asList(ss))); + } +} +
diff --git a/annotation-file-utilities/tests/LambdaExpression.jaif b/annotation-file-utilities/tests/LambdaExpression.jaif new file mode 100644 index 0000000..28412a7 --- /dev/null +++ b/annotation-file-utilities/tests/LambdaExpression.jaif
@@ -0,0 +1,34 @@ +package: +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) +annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) +annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) + +class LambdaExpression: + + field f2: + lambda *0: + parameter 0: + type: @A + + field f3: + lambda *0: + parameter 0: + type: @A + + field f4: + lambda *0: + parameter 0: + type: @A + parameter 1: + type: @B + + field selectCommon: + lambda *0: + parameter 0: + type: @B + inner-type 0, 0: @A + parameter 1: + type: @A + inner-type 3, 0: @B + inner-type 3, 0, 2, 0: @C +
diff --git a/annotation-file-utilities/tests/LambdaExpression.java b/annotation-file-utilities/tests/LambdaExpression.java new file mode 100644 index 0000000..67e0386 --- /dev/null +++ b/annotation-file-utilities/tests/LambdaExpression.java
@@ -0,0 +1,63 @@ +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntBinaryOperator; +import java.util.function.IntFunction; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.ToIntBiFunction; + +@Target(ElementType.TYPE_USE) @interface A {} + +@Target(ElementType.TYPE_USE) @interface B {} + +@Target(ElementType.TYPE_USE) @interface C {} + +class LambdaExpression { + //Single inferred-type parameter + IntFunction<Integer> f0 = (x) -> x+1; + + //Parentheses optional for single inferred-type parameter + IntFunction<Integer> f1 = x -> x+1; + + //Single declared-type parameter, expression body + IntFunction<Integer> f2 = (int x) -> x+1; + + //Single declared-type parameter, block body + IntFunction<Integer> f3 = (int x) -> { return x+1; }; + + //Multiple declared-type parameters + IntBinaryOperator f4 = (int x, int y) -> x+y; + + //Generic argument type + static final ToIntBiFunction<String[], List<? extends CharSequence>> + selectCommon = (String[] array, + List<? extends CharSequence> list) -> + { + int total = 0; + for (int i = 0; i < array.length; i++) { + Iterator<? extends CharSequence> iter = list.iterator(); + String str = array[i]; + while (iter.hasNext()) { + CharSequence seq = iter.next(); + if (seq.toString().equals(str)) { + ++total; + iter.remove(); + break; + } + } + } + return total; + }; + + public static void main(String[] args) { + String[] ss = {"a", "b"}; + System.out.println(selectCommon.applyAsInt(args, Arrays.asList(ss))); + } +} +
diff --git a/annotation-file-utilities/tests/LocalArray.goal b/annotation-file-utilities/tests/LocalArray.goal new file mode 100644 index 0000000..204346e --- /dev/null +++ b/annotation-file-utilities/tests/LocalArray.goal
@@ -0,0 +1,9 @@ +package annotator.tests; + +public class LocalArray { + + public void foo() { + @java.lang.UnderInitialization Object @java.lang.Tainted [] o = null; + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalArray.jaif b/annotation-file-utilities/tests/LocalArray.jaif new file mode 100644 index 0000000..93df2c2 --- /dev/null +++ b/annotation-file-utilities/tests/LocalArray.jaif
@@ -0,0 +1,16 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalArray: + + method <init>()V: + + method foo()V: + local 1 #2+8: + type: @java.lang.Tainted + inner-type 0, 0: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/LocalArray.java b/annotation-file-utilities/tests/LocalArray.java new file mode 100644 index 0000000..0f23dd9 --- /dev/null +++ b/annotation-file-utilities/tests/LocalArray.java
@@ -0,0 +1,9 @@ +package annotator.tests; + +public class LocalArray { + + public void foo() { + Object[] o = null; + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalClass.goal b/annotation-file-utilities/tests/LocalClass.goal new file mode 100644 index 0000000..083a883 --- /dev/null +++ b/annotation-file-utilities/tests/LocalClass.goal
@@ -0,0 +1,66 @@ +public class LocalClass { + + @A(1) Object f; + + void m() { + class InnerLocalClass { + @A(2) Object f; + } + new Object() { + @A(3) Object f; + + class Test{ + @A(4) Object f; + + void m() { + new Object() { + @A(41) Object f; + }; + new Object() { + @A(42) Object f; + }; + } + } + }; + new Object() { + @A(31) Object f; + }; + } + + void m2() { + class InnerLocalClass { + @A(5) Object f; + + class Inner { + @A(6) Object f; + + void m() { + new Object() { + @A(7) Object f; + }; + new Object() { + @A(71) Object f; + }; + } + } + + void m() { + class OuterLocalClass { + @A(10) Object f; + } + } + } + } + + void m3() { + class OuterLocalClass { + @A(8) Object f; + + void m() { + class InnerLocalClass { + @A(9) Object f; + } + } + } + } +}
diff --git a/annotation-file-utilities/tests/LocalClass.jaif b/annotation-file-utilities/tests/LocalClass.jaif new file mode 100644 index 0000000..80b54d9 --- /dev/null +++ b/annotation-file-utilities/tests/LocalClass.jaif
@@ -0,0 +1,59 @@ +package: + annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + int value + + class LocalClass: + field f: + type: @A(1) + + class LocalClass$1InnerLocalClass: + field f: + type: @A(2) + + class LocalClass$1: + field f: + type: @A(3) + + class LocalClass$2: + field f: + type: @A(31) + + class LocalClass$1$Test: + field f: + type: @A(4) + + class LocalClass$1$Test$1: + field f: + type: @A(41) + + class LocalClass$1$Test$2: + field f: + type: @A(42) + + class LocalClass$2InnerLocalClass: + field f: + type: @A(5) + + class LocalClass$2InnerLocalClass$Inner: + field f: + type: @A(6) + + class LocalClass$2InnerLocalClass$Inner$1: + field f: + type: @A(7) + + class LocalClass$2InnerLocalClass$Inner$2: + field f: + type: @A(71) + + class LocalClass$2InnerLocalClass$1OuterLocalClass: + field f: + type: @A(10) + + class LocalClass$1OuterLocalClass: + field f: + type: @A(8) + + class LocalClass$1OuterLocalClass$1InnerLocalClass: + field f: + type: @A(9)
diff --git a/annotation-file-utilities/tests/LocalClass.java b/annotation-file-utilities/tests/LocalClass.java new file mode 100644 index 0000000..ccb7483 --- /dev/null +++ b/annotation-file-utilities/tests/LocalClass.java
@@ -0,0 +1,66 @@ +public class LocalClass { + + Object f; + + void m() { + class InnerLocalClass { + Object f; + } + new Object() { + Object f; + + class Test{ + Object f; + + void m() { + new Object() { + Object f; + }; + new Object() { + Object f; + }; + } + } + }; + new Object() { + Object f; + }; + } + + void m2() { + class InnerLocalClass { + Object f; + + class Inner { + Object f; + + void m() { + new Object() { + Object f; + }; + new Object() { + Object f; + }; + } + } + + void m() { + class OuterLocalClass { + Object f; + } + } + } + } + + void m3() { + class OuterLocalClass { + Object f; + + void m() { + class InnerLocalClass { + Object f; + } + } + } + } +}
diff --git a/annotation-file-utilities/tests/LocalGeneric.goal b/annotation-file-utilities/tests/LocalGeneric.goal new file mode 100644 index 0000000..a09fe9f --- /dev/null +++ b/annotation-file-utilities/tests/LocalGeneric.goal
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; + +public class LocalGeneric { + public void foo() { + @java.lang.UnderInitialization List<@java.lang.Tainted Integer> var = null; + System.out.println(var); + } +}
diff --git a/annotation-file-utilities/tests/LocalGeneric.jaif b/annotation-file-utilities/tests/LocalGeneric.jaif new file mode 100644 index 0000000..a09efeb --- /dev/null +++ b/annotation-file-utilities/tests/LocalGeneric.jaif
@@ -0,0 +1,16 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalGeneric: + + method <init>()V: + + method foo()V: + local 1 #2+8: + type: @java.lang.UnderInitialization + inner-type 3, 0: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/LocalGeneric.java b/annotation-file-utilities/tests/LocalGeneric.java new file mode 100644 index 0000000..a6fc891 --- /dev/null +++ b/annotation-file-utilities/tests/LocalGeneric.java
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; + +public class LocalGeneric { + public void foo() { + List<Integer> var = null; + System.out.println(var); + } +}
diff --git a/annotation-file-utilities/tests/LocalGenericShadow.goal b/annotation-file-utilities/tests/LocalGenericShadow.goal new file mode 100644 index 0000000..a039ac5 --- /dev/null +++ b/annotation-file-utilities/tests/LocalGenericShadow.goal
@@ -0,0 +1,13 @@ +package annotator.tests; + +import java.util.List; + + +public class LocalGenericShadow { + public List<String> foo = null; + + public void method() { + @java.lang.UnderInitialization List<Integer> foo = null; + System.out.println(foo); + } +}
diff --git a/annotation-file-utilities/tests/LocalGenericShadow.jaif b/annotation-file-utilities/tests/LocalGenericShadow.jaif new file mode 100644 index 0000000..0ce82b9 --- /dev/null +++ b/annotation-file-utilities/tests/LocalGenericShadow.jaif
@@ -0,0 +1,14 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalGenericShadow: + + field foo: + + method <init>()V: + + method method()V: + local 1 #2+8: + type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/LocalGenericShadow.java b/annotation-file-utilities/tests/LocalGenericShadow.java new file mode 100644 index 0000000..8c6161f --- /dev/null +++ b/annotation-file-utilities/tests/LocalGenericShadow.java
@@ -0,0 +1,13 @@ +package annotator.tests; + +import java.util.List; + + +public class LocalGenericShadow { + public List<String> foo = null; + + public void method() { + List<Integer> foo = null; + System.out.println(foo); + } +}
diff --git a/annotation-file-utilities/tests/LocalMultiple.goal b/annotation-file-utilities/tests/LocalMultiple.goal new file mode 100644 index 0000000..95e0c65 --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultiple.goal
@@ -0,0 +1,20 @@ +package annotator.tests; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LocalMultiple { + public void foo(Object o) { + List myList = null; + + if (myList.size() != 0) { + /* @UnderInitialization*/ @java.lang.UnderInitialization Set localVar = null; + myList.add(localVar); + } else { + /* @Tainted*/ @java.lang.Tainted Set localVar = null; + myList.add(localVar); + } + foo(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalMultiple.jaif b/annotation-file-utilities/tests/LocalMultiple.jaif new file mode 100644 index 0000000..a94da14 --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultiple.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalMultiple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + local 3 #13+8: + type: @java.lang.UnderInitialization + local 3 #26+8: + type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/LocalMultiple.java b/annotation-file-utilities/tests/LocalMultiple.java new file mode 100644 index 0000000..3b8f205 --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultiple.java
@@ -0,0 +1,20 @@ +package annotator.tests; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LocalMultiple { + public void foo(Object o) { + List myList = null; + + if (myList.size() != 0) { + /* @UnderInitialization*/ Set localVar = null; + myList.add(localVar); + } else { + /* @Tainted*/ Set localVar = null; + myList.add(localVar); + } + foo(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethods.goal b/annotation-file-utilities/tests/LocalMultipleManyMethods.goal new file mode 100644 index 0000000..4689fd9 --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultipleManyMethods.goal
@@ -0,0 +1,34 @@ +package annotator.tests; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LocalMultipleManyMethods { + public void foo(Object o) { + List myList = null; + + if (myList.size() != 0) { + @java.lang.C Set localVar = null; + myList.add(localVar); + } else { + @java.lang.B Set localVar = null; + myList.add(localVar); + } + foo(o); + } + + public void foo(Object[] o) { + List myList = null; + + if (myList.size() != 0) { + @java.lang.A Set localVar = null; + myList.add(localVar); + } else { + @java.lang.D Set localVar = null; + myList.add(localVar); + } + foo(o); + } +} +
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethods.jaif b/annotation-file-utilities/tests/LocalMultipleManyMethods.jaif new file mode 100644 index 0000000..89b45ea --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultipleManyMethods.jaif
@@ -0,0 +1,29 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalMultipleManyMethods: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + local 3 #13+8: + type: @java.lang.C + local 3 #26+8: + type: @java.lang.B + + method foo([Ljava/lang/Object;)V: + local 3 #13+8: + type: @java.lang.A + local 3 #26+8: + type: @java.lang.D +
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethods.java b/annotation-file-utilities/tests/LocalMultipleManyMethods.java new file mode 100644 index 0000000..d643fa6 --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultipleManyMethods.java
@@ -0,0 +1,34 @@ +package annotator.tests; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class LocalMultipleManyMethods { + public void foo(Object o) { + List myList = null; + + if (myList.size() != 0) { + Set localVar = null; + myList.add(localVar); + } else { + Set localVar = null; + myList.add(localVar); + } + foo(o); + } + + public void foo(Object[] o) { + List myList = null; + + if (myList.size() != 0) { + Set localVar = null; + myList.add(localVar); + } else { + Set localVar = null; + myList.add(localVar); + } + foo(o); + } +} +
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.goal b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.goal new file mode 100644 index 0000000..404556a --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.goal
@@ -0,0 +1,38 @@ +package annotator.tests; + +import java.util.List; +import java.util.Set; + +public class LocalMultipleManyMethodsShifted { + + public void foo(Object o) { + List myList = null; + + myList.add(myList); + myList.remove(myList); + + if (myList.size() != 0) { + @java.lang.C Set localVar = null; + foo(localVar); + System.out.println(localVar); + myList.add(localVar); + } else { + Set localVar = null; + myList.add(localVar); + } + foo(o); + } + + public void foo(Object[] o) { + List myList = null; + + if (myList.size() != 0) { + @java.lang.D Set localVar = null; + myList.add(localVar); + } else { + @java.lang.A Set localVar = null; + myList.add(localVar); + } + foo(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.jaif b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.jaif new file mode 100644 index 0000000..a0d2249 --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.jaif
@@ -0,0 +1,28 @@ +package java.lang: +annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + + +package annotator.tests: +class LocalMultipleManyMethodsShifted: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + local 3 #29+20: + type: @java.lang.C + local 3 #54+8: + + method foo([Ljava/lang/Object;)V: + local 3 #13+8: + type: @java.lang.D + local 3 #26+8: + type: @java.lang.A
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.java b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.java new file mode 100644 index 0000000..7b5cb5e --- /dev/null +++ b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.java
@@ -0,0 +1,38 @@ +package annotator.tests; + +import java.util.List; +import java.util.Set; + +public class LocalMultipleManyMethodsShifted { + + public void foo(Object o) { + List myList = null; + + myList.add(myList); + myList.remove(myList); + + if (myList.size() != 0) { + Set localVar = null; + foo(localVar); + System.out.println(localVar); + myList.add(localVar); + } else { + Set localVar = null; + myList.add(localVar); + } + foo(o); + } + + public void foo(Object[] o) { + List myList = null; + + if (myList.size() != 0) { + Set localVar = null; + myList.add(localVar); + } else { + Set localVar = null; + myList.add(localVar); + } + foo(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalSimple.goal b/annotation-file-utilities/tests/LocalSimple.goal new file mode 100644 index 0000000..9260ad3 --- /dev/null +++ b/annotation-file-utilities/tests/LocalSimple.goal
@@ -0,0 +1,8 @@ +package annotator.tests; + +public class LocalSimple { + public void foo() { + @java.lang.Tainted Object o = null; + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalSimple.jaif b/annotation-file-utilities/tests/LocalSimple.jaif new file mode 100644 index 0000000..7d112d4 --- /dev/null +++ b/annotation-file-utilities/tests/LocalSimple.jaif
@@ -0,0 +1,12 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalSimple: + + method <init>()V: + + method foo()V: + local 1 #2+8: + type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/LocalSimple.java b/annotation-file-utilities/tests/LocalSimple.java new file mode 100644 index 0000000..90fefe2 --- /dev/null +++ b/annotation-file-utilities/tests/LocalSimple.java
@@ -0,0 +1,8 @@ +package annotator.tests; + +public class LocalSimple { + public void foo() { + Object o = null; + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/LocalSimpleMultiple.goal b/annotation-file-utilities/tests/LocalSimpleMultiple.goal new file mode 100644 index 0000000..69208fb --- /dev/null +++ b/annotation-file-utilities/tests/LocalSimpleMultiple.goal
@@ -0,0 +1,18 @@ +package annotator.tests; + +import java.util.List; + +public class LocalSimpleMultiple { + public void foo() { + @java.lang.UnderInitialization Object o = null; + System.out.println(o); + @java.lang.Tainted List list = null; + bar(list); + bar(o); + } + + public void bar(Object o) { + @java.lang.Tainted LocalSimpleMultiple second = null; + bar(second); + } +}
diff --git a/annotation-file-utilities/tests/LocalSimpleMultiple.jaif b/annotation-file-utilities/tests/LocalSimpleMultiple.jaif new file mode 100644 index 0000000..49d04ce --- /dev/null +++ b/annotation-file-utilities/tests/LocalSimpleMultiple.jaif
@@ -0,0 +1,21 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalSimpleMultiple: + + method <init>()V: + + method foo()V: + local 1 #2+20: + type: @java.lang.UnderInitialization + local 2 #11+11: + type: @java.lang.Tainted + + method bar(Ljava/lang/Object;)V: + local 2 #2+6: + type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/LocalSimpleMultiple.java b/annotation-file-utilities/tests/LocalSimpleMultiple.java new file mode 100644 index 0000000..ffb5fe7 --- /dev/null +++ b/annotation-file-utilities/tests/LocalSimpleMultiple.java
@@ -0,0 +1,18 @@ +package annotator.tests; + +import java.util.List; + +public class LocalSimpleMultiple { + public void foo() { + Object o = null; + System.out.println(o); + List list = null; + bar(list); + bar(o); + } + + public void bar(Object o) { + LocalSimpleMultiple second = null; + bar(second); + } +}
diff --git a/annotation-file-utilities/tests/Makefile b/annotation-file-utilities/tests/Makefile new file mode 100644 index 0000000..80ba9de --- /dev/null +++ b/annotation-file-utilities/tests/Makefile
@@ -0,0 +1,104 @@ +# Very rough testing framework for the annotator. Running 'make all' will +# look for all myClass.goal files in this directory, run the annotator on the +# corresponding .jaif and .java files, and then output the difference in a +# myClass.diff file in this directory. +# +# To test just one file, use (for example) 'make myClass.diff'. + +# Put user-specific changes in your own Makefile.user. +# Make will silently continue if that file does not exist. +-include Makefile.user + +# Override these in Makefile.user if the java and javac commands are not on +# your execution path. Example from Makefile.user: +# JAVA=${JAVA_HOME}/bin/java +# JAVAC=${JAVA_HOME}/bin/javac +export JAVA?=java -ea +export JAVAC?=javac +export XJAVAC?=javac + +export SHELL=/bin/bash -o pipefail + + +DIFFS := $(wildcard *.goal) +DISABLED := $(shell grep -le "@skip-test" $(DIFFS)) +FILTERED := $(filter-out $(DISABLED),$(DIFFS)) +DIFFS := $(patsubst %.goal, %.diff, $(FILTERED)) + +DEBUG := +# Use this to enable some debugging. +# DEBUG := --debug + +default : all + +.PHONY: all +all : $(DIFFS) abbreviated ad-hoc system-test source-extension results + +.PHONY: abbreviated +abbreviated: + ${MAKE} -C abbreviated + +.PHONY: ad-hoc +ad-hoc: + ${MAKE} -C ad-hoc + +.PHONY: source-extension +source-extension: + ${MAKE} -C source-extension + +.PHONY: system-test +system-test: + ${MAKE} -C system-test + +# Display results of all .diff files. +.PHONY: results +results: bin/VerifyDiffs.class + @echo "" + @echo "=== RESULTS ===" + @echo "" + @$(JAVA) -cp bin VerifyDiffs --show_all + +# Remakes the little java program that checks and compares diffs +bin/VerifyDiffs.class : VerifyDiffs.java + @$(JAVAC) -g -cp ../bincompile -d bin VerifyDiffs.java + +# Compiles all the test cases (be verbose about this). +compile : + mkdir -p bin + $(XJAVAC) -g -cp ../bin -d bin *.java + +.PRECIOUS : bin/annotator/tests/%.class +bin/annotator/tests/%.class: %.java + mkdir -p bin +# Added "-Xlint:-options" to see if it permits Jenkins job to succeed, due to +# problem "target value 1.8 is obsolete and will be removed in a future release" + $(XJAVAC) -Xlint:-options -g -cp bin:../annotation-file-utilities.jar -d bin -sourcepath . $*.java + +# Actually runs the annotator to create the annotated java file. +.PRECIOUS: %.output +%.output: %.jaif %.java bin/annotator/tests/%.class ../lib/plume-core.jar ../bin ../annotation-file-utilities.jar + $(JAVA) \ + -cp ../bin:../annotation-file-utilities.jar:bin \ + annotator.Main \ + ${DEBUG} \ + --abbreviate=false \ + -d $*-output \ + $*.jaif \ + $*.java \ + 2>&1 | tee $*.log + find "$*-output" -name '*.java' -print | xargs cat > "$*.output" + rm -rf $*-output + +# Compare the output of the annotator and the goal file. +%.diff: %.goal %.output + -diff -u $*.goal $*.output >& $*.diff + +# Remove all .diff, .log files from the tests directory. +.PHONY: clean +clean : + rm -rf bin + rm -f *.diff + rm -f *.log + rm -f *.output + (cd abbreviated && make clean) + (cd ad-hoc && make clean)
diff --git a/annotation-file-utilities/tests/MatchReturnValue.goal b/annotation-file-utilities/tests/MatchReturnValue.goal new file mode 100644 index 0000000..372f553 --- /dev/null +++ b/annotation-file-utilities/tests/MatchReturnValue.goal
@@ -0,0 +1,10 @@ +package annotator.tests; + +public class MatchReturnValue<T> { + + @java.lang.A + public MatchReturnValue<T> clone() { + return this; + } + +}
diff --git a/annotation-file-utilities/tests/MatchReturnValue.jaif b/annotation-file-utilities/tests/MatchReturnValue.jaif new file mode 100644 index 0000000..04df367 --- /dev/null +++ b/annotation-file-utilities/tests/MatchReturnValue.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @A: + +annotation @ShouldNotAppearInOutput: + +package annotator.tests: +class MatchReturnValue: + + method clone()Lannotator.tests.MatchReturnValue;: @A + method clone()Ljava.lang.Object;: @ShouldNotAppearInOutput
diff --git a/annotation-file-utilities/tests/MatchReturnValue.java b/annotation-file-utilities/tests/MatchReturnValue.java new file mode 100644 index 0000000..968de0c --- /dev/null +++ b/annotation-file-utilities/tests/MatchReturnValue.java
@@ -0,0 +1,9 @@ +package annotator.tests; + +public class MatchReturnValue<T> { + + public MatchReturnValue<T> clone() { + return this; + } + +}
diff --git a/annotation-file-utilities/tests/MemberReference.goal b/annotation-file-utilities/tests/MemberReference.goal new file mode 100644 index 0000000..a28db8b --- /dev/null +++ b/annotation-file-utilities/tests/MemberReference.goal
@@ -0,0 +1,101 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +//import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import java.util.function.UnaryOperator; + +@Target(ElementType.TYPE_USE) @interface A {} +@Target(ElementType.TYPE_USE) @interface B {} +@Target(ElementType.TYPE_USE) @interface C {} +@Target(ElementType.TYPE_USE) @interface D {} + +@skip-test +public class MemberReference<T> { + class Inner<U> implements Map.Entry<T, U> { + @Override + public T getKey() { return null; } + @Override + public U getValue() { return null; } + @Override + public U setValue(U u) { throw new UnsupportedOperationException(); } + } + + boolean test = false; + List list = new ArrayList<T>(); + Collection<T>[] foo; + + <U extends T> MemberReference(U bar) { + foo = new ArrayList[1]; + foo[0] = new ArrayList<T>(1); + foo[0].add(bar); + } + + // instance method + ToIntFunction<String> o1 = @A String::length; + + // static method + LongSupplier o2 = @A System::currentTimeMillis; + + // explicit type args + ToIntFunction<List<String>> o3 = @A List<@B String>::size; + + // inferred type args + ToIntFunction<List<String>> o4 = @A List::size; + + UnaryOperator<Integer[]> o5 = @A Integer @B []::clone; + + Function<T, String> o6 = @A T::toString; + + //IntSupplier o7 = ((String) "abc")::length; + //IntSupplier o8 = ((List<String>) foo[0])::size; + + Supplier<Iterator<T>> o9 = + (test ? list : Collections.<@A T>emptyList()) :: iterator; + + // constructor for parameterized type + Supplier<ArrayList<String>> o10 = + @A ArrayList<@B String>::new; + + // inferred type arguments for generic class + Supplier<ArrayList<String>> o11 = + @A ArrayList::new; + + // explicit type arguments for generic constructor + IntFunction<List<Integer>> o12 = + @A ArrayList<@B Integer>::<@C Integer>new; + + // generic class + constructor + Function<String, MemberReference<CharSequence>> o13 = + @A MemberReference<@B CharSequence>::<@C String>new; + + // inner class constructor + Supplier<MemberReference<Object>.Inner<Long>> o14 = + @A MemberReference<@B Object>.Inner<@C Long>::<@D String>new; + + // array creation + Function<Integer, Integer[]> o15 = + @A Integer @B []::new; + + //Consumer<String> o16 = System.out::println; + //Supplier<String> o17 = ((Object) super)::toString; + //Supplier<String> o18 = ((Object) MemberReference.super)::toString; + + public static void main(String[] args) { + String[] ss = {"a", "b"}; + System.out.println(java.util.Arrays.asList(ss).stream() + .map(@A String::toUpperCase) + .collect(java.util.stream.Collectors.joining(" "))); + } +} +
diff --git a/annotation-file-utilities/tests/MemberReference.jaif b/annotation-file-utilities/tests/MemberReference.jaif new file mode 100644 index 0000000..ea9ae4a --- /dev/null +++ b/annotation-file-utilities/tests/MemberReference.jaif
@@ -0,0 +1,62 @@ +package : +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) +annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) +annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) +annotation @D: @java.lang.annotation.Target(value={TYPE_USE}) + +class MemberReference: + + field o1: + reference *0: @A + + field o2: + reference *0: @A + + field o3: + reference *0: @A + inner-type 3, 0: @B + + field o4: + reference *0: @A + + field o5: + reference *0: @B + inner-type 0, 0: @A + + field o6: + reference *0: @A + + field o9: + call *0: + typearg 0: @A + + field o10: + reference *0: @A + inner-type 3, 0: @B + + field o11: + reference *0: @A + + field o12: + reference *0: @A + inner-type 3, 0: @B + typearg 0: @C + + field o13: + reference *0: @A + inner-type 3, 0: @B + typearg 0: @C + + field o14: + reference *0: @A + inner-type 3, 0: @B + inner-type 1, 0, 3, 0: @C + typearg 0: @D + + field o15: + reference *0: @B + inner-type 0, 0: @A + + method main([Ljava/lang/String;)V: + reference #27: @A +
diff --git a/annotation-file-utilities/tests/MemberReference.java b/annotation-file-utilities/tests/MemberReference.java new file mode 100644 index 0000000..23b0f22 --- /dev/null +++ b/annotation-file-utilities/tests/MemberReference.java
@@ -0,0 +1,98 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.IntSupplier; +import java.util.function.LongSupplier; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; +import java.util.function.UnaryOperator; + +@Target(ElementType.TYPE_USE) @interface A {} +@Target(ElementType.TYPE_USE) @interface B {} +@Target(ElementType.TYPE_USE) @interface C {} +@Target(ElementType.TYPE_USE) @interface D {} + +public class MemberReference<T> { + class Inner<U> implements Map.Entry<T, U> { + @Override + public T getKey() { return null; } + @Override + public U getValue() { return null; } + @Override + public U setValue(U u) { throw new UnsupportedOperationException(); } + } + + boolean test = false; + List list = new ArrayList<T>(); + Collection<T>[] foo; + + <U extends T> MemberReference(U bar) { + foo = new ArrayList[1]; + foo[0] = new ArrayList<T>(1); + foo[0].add(bar); + } + + // instance method + ToIntFunction<String> o1 = String::length; + + // static method + LongSupplier o2 = System::currentTimeMillis; + + // explicit type args + ToIntFunction<List<String>> o3 = List<String>::size; + + // inferred type args + ToIntFunction<List<String>> o4 = List::size; + + UnaryOperator<Integer[]> o5 = Integer[]::clone; + + Function<T, String> o6 = T::toString; + + //IntSupplier o7 = ((String) "abc")::length; + //IntSupplier o8 = ((List<String>) foo[0])::size; + + Supplier<Iterator<T>> o9 = + (test ? list : Collections.<T>emptyList()) :: iterator; + + // constructor for parameterized type + Supplier<ArrayList<String>> o10 = + ArrayList<String>::new; + + // inferred type arguments for generic class + Supplier<ArrayList<String>> o11 = + ArrayList::new; + + // explicit type arguments for generic constructor + IntFunction<List<Integer>> o12 = + ArrayList<Integer>::<Integer>new; + + // generic class + constructor + Function<String, MemberReference<CharSequence>> o13 = + MemberReference<CharSequence>::<String>new; + + // inner class constructor + Supplier<MemberReference<Object>.Inner<Long>> o14 = + MemberReference<Object>.Inner<Long>::<String>new; + + // array creation + Function<Integer, Integer[]> o15 = + Integer[]::new; + + //Consumer<String> o16 = System.out::println; + //Supplier<String> o17 = ((Object) super)::toString; + //Supplier<String> o18 = ((Object) MemberReference.super)::toString; + + public static void main(String[] args) { + String[] ss = {"a", "b"}; + System.out.println(java.util.Arrays.asList(ss).stream() + .map(String::toUpperCase) + .collect(java.util.stream.Collectors.joining(" "))); + } +}
diff --git a/annotation-file-utilities/tests/MemberSelectTypes.goal b/annotation-file-utilities/tests/MemberSelectTypes.goal new file mode 100644 index 0000000..9fd7b6d --- /dev/null +++ b/annotation-file-utilities/tests/MemberSelectTypes.goal
@@ -0,0 +1,37 @@ +import java.util.Map; + +public class MemberSelectTypes<T extends java.lang.@Anno(0) Object & java.lang.@Anno(1) Comparable> + extends java.lang.@Anno(2) Object implements java.io.@Anno(3) Serializable { + + class Inner { + void m(@A MemberSelectTypes<T>.Inner this) {} + } + + static class StaticInner { + void m(MemberSelectTypes.@B StaticInner this) {} + } + + java.lang.@C Object o; + java.util.Map.@D Entry<java.lang.@E String, java.util.Map.@F Entry<Object, Object>> m1; + Map.@G Entry<java.lang.@H String, Map.@I Entry<Object, Object>> m2; + Map<@H MemberSelectTypes.Inner, MemberSelectTypes.@I StaticInner> m3; + Map.@G Entry<Map.@H Entry<Map.@I Entry<Map.@K Entry<Object, Object>, Object>, Object>, Object> m4; + @J MemberSelectTypes.Inner i; + MemberSelectTypes.@K StaticInner s; + + java.lang.@L Object m(java.lang.@M Object o, @M MemberSelectTypes.Inner i, MemberSelectTypes.@M StaticInner s) { + java.lang.@N Object o2 = (java.lang.@O Object) o; + @N MemberSelectTypes.Inner i2 = (@O MemberSelectTypes.Inner) i; + MemberSelectTypes.@N StaticInner s2 = (MemberSelectTypes.@O StaticInner) s; + o2 = new java.lang.@P Object(); + i2 = new @P MemberSelectTypes.Inner(); + s2 = new MemberSelectTypes.@P StaticInner(); + java.lang.@Q Object[] os = new java.lang.@R Object[1]; + @Q MemberSelectTypes.Inner[] is = new @R MemberSelectTypes.Inner[1]; + MemberSelectTypes.@Q StaticInner[] ss = new MemberSelectTypes.@R StaticInner[1]; + boolean b = o instanceof java.lang.@S Object; + b = o instanceof @S MemberSelectTypes.Inner; + b = o instanceof MemberSelectTypes.@S StaticInner; + return o2; + } +}
diff --git a/annotation-file-utilities/tests/MemberSelectTypes.jaif b/annotation-file-utilities/tests/MemberSelectTypes.jaif new file mode 100644 index 0000000..9021bba --- /dev/null +++ b/annotation-file-utilities/tests/MemberSelectTypes.jaif
@@ -0,0 +1,101 @@ +package: + annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @D: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @E: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @F: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @G: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @H: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @I: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @J: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @K: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @L: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @M: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @N: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @O: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @P: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @Q: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @R: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @S: @java.lang.annotation.Target(value={TYPE_USE}) + annotation @Anno: @java.lang.annotation.Target(value={TYPE_USE}) + int value + + class MemberSelectTypes$Inner: + method m()V: + receiver: @A + + class MemberSelectTypes$StaticInner: + method m()V: + receiver: @B + + class MemberSelectTypes: + bound 0 & 0: @Anno(0) + bound 0 & 1: @Anno(1) + extends: @Anno(2) + implements 0: @Anno(3) + field o: + type: @C + field m1: + type: @D + inner-type 3, 0: @E + inner-type 3, 1: @F + field m2: + type: @G + inner-type 3, 0: @H + inner-type 3, 1: @I + + field m3: + type: + inner-type 3, 0: @H + inner-type 3, 1: @I + + field m4: + type: @G + inner-type 3, 0: @H + inner-type 3, 0, 3, 0: @I + inner-type 3, 0, 3, 0, 3, 0: @K + + field i: + type: @J + field s: + type: @K + + method m(Ljava/lang/Object;LMemberSelectTypes$Inner;LMemberSelectTypes$StaticInner;)Ljava/lang/Object;: + return: @L + parameter 0: + type: @M + parameter 1: + type: @M + parameter 2: + type: @M + local o2: + type: @N + local i2: + type: @N + local s2: + type: @N + typecast *0: @O + typecast *1: @O + typecast *2: @O + new *0: @P + new *1: @P + new *2: @P + local os: + type: + inner-type 0, 0: @Q + local is: + type: + inner-type 0, 0: @Q + local ss: + type: + inner-type 0, 0: @Q + new *3: + inner-type 0, 0: @R + new *4: + inner-type 0, 0: @R + new *5: + inner-type 0, 0: @R + instanceof *0: @S + instanceof *1: @S + instanceof *2: @S
diff --git a/annotation-file-utilities/tests/MemberSelectTypes.java b/annotation-file-utilities/tests/MemberSelectTypes.java new file mode 100644 index 0000000..532dc33 --- /dev/null +++ b/annotation-file-utilities/tests/MemberSelectTypes.java
@@ -0,0 +1,37 @@ +import java.util.Map; + +public class MemberSelectTypes<T extends java.lang.Object & java.lang.Comparable> + extends java.lang.Object implements java.io.Serializable { + + class Inner { + void m(MemberSelectTypes<T>.Inner this) {} + } + + static class StaticInner { + void m(MemberSelectTypes.StaticInner this) {} + } + + java.lang.Object o; + java.util.Map.Entry<java.lang.String, java.util.Map.Entry<Object, Object>> m1; + Map.Entry<java.lang.String, Map.Entry<Object, Object>> m2; + Map<MemberSelectTypes.Inner, MemberSelectTypes.StaticInner> m3; + Map.Entry<Map.Entry<Map.Entry<Map.Entry<Object, Object>, Object>, Object>, Object> m4; + MemberSelectTypes.Inner i; + MemberSelectTypes.StaticInner s; + + java.lang.Object m(java.lang.Object o, MemberSelectTypes.Inner i, MemberSelectTypes.StaticInner s) { + java.lang.Object o2 = (java.lang.Object) o; + MemberSelectTypes.Inner i2 = (MemberSelectTypes.Inner) i; + MemberSelectTypes.StaticInner s2 = (MemberSelectTypes.StaticInner) s; + o2 = new java.lang.Object(); + i2 = new MemberSelectTypes.Inner(); + s2 = new MemberSelectTypes.StaticInner(); + java.lang.Object[] os = new java.lang.Object[1]; + MemberSelectTypes.Inner[] is = new MemberSelectTypes.Inner[1]; + MemberSelectTypes.StaticInner[] ss = new MemberSelectTypes.StaticInner[1]; + boolean b = o instanceof java.lang.Object; + b = o instanceof MemberSelectTypes.Inner; + b = o instanceof MemberSelectTypes.StaticInner; + return o2; + } +}
diff --git a/annotation-file-utilities/tests/MethodCompoundType.goal b/annotation-file-utilities/tests/MethodCompoundType.goal new file mode 100644 index 0000000..bbb830e --- /dev/null +++ b/annotation-file-utilities/tests/MethodCompoundType.goal
@@ -0,0 +1,7 @@ +import java.util.Map; + +public class MethodCompoundType { + public Map @A [] m() { + return null; + } +}
diff --git a/annotation-file-utilities/tests/MethodCompoundType.jaif b/annotation-file-utilities/tests/MethodCompoundType.jaif new file mode 100644 index 0000000..c8eea69 --- /dev/null +++ b/annotation-file-utilities/tests/MethodCompoundType.jaif
@@ -0,0 +1,6 @@ +package: + annotation @A: @Target(TYPE_USE) + + class MethodCompoundType: + method m()[Ljava/util/Map;: + return: @A
diff --git a/annotation-file-utilities/tests/MethodCompoundType.java b/annotation-file-utilities/tests/MethodCompoundType.java new file mode 100644 index 0000000..227d9d4 --- /dev/null +++ b/annotation-file-utilities/tests/MethodCompoundType.java
@@ -0,0 +1,7 @@ +import java.util.Map; + +public class MethodCompoundType { + public Map[] m() { + return null; + } +}
diff --git a/annotation-file-utilities/tests/MethodMultiple.goal b/annotation-file-utilities/tests/MethodMultiple.goal new file mode 100644 index 0000000..9832c84 --- /dev/null +++ b/annotation-file-utilities/tests/MethodMultiple.goal
@@ -0,0 +1,11 @@ +package annotator.tests; + +public class MethodMultiple { + public String foo() { + return null; + } + + public @java.lang.UnderInitialization String foo(String s) { + return null; + } +}
diff --git a/annotation-file-utilities/tests/MethodMultiple.jaif b/annotation-file-utilities/tests/MethodMultiple.jaif new file mode 100644 index 0000000..34bbd89 --- /dev/null +++ b/annotation-file-utilities/tests/MethodMultiple.jaif
@@ -0,0 +1,13 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class MethodMultiple: + + method <init>()V: + + method foo()Ljava/lang/String;: + + method foo(Ljava/lang/String;)Ljava/lang/String;: + return: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/MethodMultiple.java b/annotation-file-utilities/tests/MethodMultiple.java new file mode 100644 index 0000000..c717aea --- /dev/null +++ b/annotation-file-utilities/tests/MethodMultiple.java
@@ -0,0 +1,11 @@ +package annotator.tests; + +public class MethodMultiple { + public String foo() { + return null; + } + + public String foo(String s) { + return null; + } +}
diff --git a/annotation-file-utilities/tests/NewArray.goal b/annotation-file-utilities/tests/NewArray.goal new file mode 100644 index 0000000..f7ffdf1 --- /dev/null +++ b/annotation-file-utilities/tests/NewArray.goal
@@ -0,0 +1,33 @@ +import java.util.ArrayList; +import java.util.List; + +public class NewArray { + private static final int MAX_HOOKS = 10; + private static final Runnable[] hooks = new @Nullable Runnable[MAX_HOOKS]; + + String[] names01 = new @X String[12]; + + String[] names02 = new @X String[] { "Alice", "Bob"}; + + String[] names03 = new @X String[] { "Alice", "Bob"}; + + static final int[] table1 = new @A int @B [] {0, 1}; + static final int table2[] = new @A int @B [] {0, 1}; + + String[][][][][] names0 = new String @Z [11][12][13][14][15]; + String[][][][][] names1 = new String[11] @A [12][13][14][15]; + String[][][][][] names2 = new String[11][12] @B [13][14][15]; + String[][][][][] names3 = new String[11][12][13] @C [14][15]; + String[][][][][] names4 = new String[11][12][13][14] @D [15]; + String[][][][][] names5 = new @E String[11][12][13][14][15]; + + Object names10 = new String @Z [][][][][] { { { }}}; + Object names11 = new String[] @A [][][][] { { { }}}; + Object names12 = new String[][] @B [][][] { { { }}}; + Object names13 = new String[][][] @C [][] { { { }}}; + Object names14 = new String[][][][] @D [] { { { }}}; + Object names15 = new @E String[][][][][] { { { }}}; + + @Z + List<@A ?> @B [] lists = new @C ArrayList<@D ?> @E [2]; +}
diff --git a/annotation-file-utilities/tests/NewArray.jaif b/annotation-file-utilities/tests/NewArray.jaif new file mode 100644 index 0000000..56befcd --- /dev/null +++ b/annotation-file-utilities/tests/NewArray.jaif
@@ -0,0 +1,80 @@ +package: +annotation @Z: +annotation @A: +annotation @B: +annotation @C: +annotation @D: +annotation @E: +annotation @X: +annotation @Nullable: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME) + +class NewArray: + +field names01: +new *0: +inner-type 0, 0: @X + +field names02: +new *0: +inner-type 0, 0: @X + +field names03: +new *0: +inner-type 0, 0: @X + +field table1: +new *0: @B +inner-type 0, 0: @A + +field table2: +new *0: @B +inner-type 0, 0: @A + +field names0: +new *0: @Z +field names1: +new *0: +inner-type 0, 0: @A +field names2: +new *0: +inner-type 0, 0, 0, 0: @B +field names3: +new *0: +inner-type 0, 0, 0, 0, 0, 0: @C +field names4: +new *0: +inner-type 0, 0, 0, 0, 0, 0, 0, 0: @D +field names5: +new *0: +inner-type 0, 0, 0, 0, 0, 0, 0, 0, 0, 0: @E + +field names10: +new *0: @Z +field names11: +new *0: +inner-type 0, 0: @A +field names12: +new *0: +inner-type 0, 0, 0, 0: @B +field names13: +new *0: +inner-type 0, 0, 0, 0, 0, 0: @C +field names14: +new *0: +inner-type 0, 0, 0, 0, 0, 0, 0, 0: @D +field names15: +new *0: +inner-type 0, 0, 0, 0, 0, 0, 0, 0, 0, 0: @E + +field lists: +type: @B +inner-type 0, 0: @Z +inner-type 0, 0, 3, 0: @A +new *0: @E +inner-type 0, 0: @C +inner-type 0, 0, 3, 0: @D + +method <clinit>()V: +new #0: +inner-type 0, 0: @Nullable +
diff --git a/annotation-file-utilities/tests/NewArray.java b/annotation-file-utilities/tests/NewArray.java new file mode 100644 index 0000000..74b5412 --- /dev/null +++ b/annotation-file-utilities/tests/NewArray.java
@@ -0,0 +1,32 @@ +import java.util.ArrayList; +import java.util.List; + +public class NewArray { + private static final int MAX_HOOKS = 10; + private static final Runnable[] hooks = new Runnable[MAX_HOOKS]; + + String[] names01 = new String[12]; + + String[] names02 = { "Alice", "Bob"}; + + String[] names03 = new String[] { "Alice", "Bob"}; + + static final int[] table1 = {0, 1}; + static final int table2[] = {0, 1}; + + String[][][][][] names0 = new String[11][12][13][14][15]; + String[][][][][] names1 = new String[11][12][13][14][15]; + String[][][][][] names2 = new String[11][12][13][14][15]; + String[][][][][] names3 = new String[11][12][13][14][15]; + String[][][][][] names4 = new String[11][12][13][14][15]; + String[][][][][] names5 = new String[11][12][13][14][15]; + + Object names10 = new String[][][][][] { { { }}}; + Object names11 = new String[][][][][] { { { }}}; + Object names12 = new String[][][][][] { { { }}}; + Object names13 = new String[][][][][] { { { }}}; + Object names14 = new String[][][][][] { { { }}}; + Object names15 = new String[][][][][] { { { }}}; + + List<?>[] lists = new ArrayList<?>[2]; +}
diff --git a/annotation-file-utilities/tests/NewGeneric.goal b/annotation-file-utilities/tests/NewGeneric.goal new file mode 100644 index 0000000..2b42309 --- /dev/null +++ b/annotation-file-utilities/tests/NewGeneric.goal
@@ -0,0 +1,13 @@ +package annotator.tests; + +import java.util.List; + +public class NewGeneric { + public void foo(Object o) { + List<NewGeneric> varOne = (@java.lang.Tainted List<@java.lang.UnderInitialization NewGeneric>) o; + + NewGeneric varTwo = (@java.lang.UnderInitialization NewGeneric) varOne; + + varTwo.foo(varOne); + } +}
diff --git a/annotation-file-utilities/tests/NewGeneric.jaif b/annotation-file-utilities/tests/NewGeneric.jaif new file mode 100644 index 0000000..3b4e91a --- /dev/null +++ b/annotation-file-utilities/tests/NewGeneric.jaif
@@ -0,0 +1,16 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class NewGeneric: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + typecast #1: @java.lang.Tainted + inner-type 3, 0: @java.lang.UnderInitialization + typecast #6: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/NewGeneric.java b/annotation-file-utilities/tests/NewGeneric.java new file mode 100644 index 0000000..10155e9 --- /dev/null +++ b/annotation-file-utilities/tests/NewGeneric.java
@@ -0,0 +1,13 @@ +package annotator.tests; + +import java.util.List; + +public class NewGeneric { + public void foo(Object o) { + List<NewGeneric> varOne = (List<NewGeneric>) o; + + NewGeneric varTwo = (NewGeneric) varOne; + + varTwo.foo(varOne); + } +}
diff --git a/annotation-file-utilities/tests/NewInAnnotatedVariable.goal b/annotation-file-utilities/tests/NewInAnnotatedVariable.goal new file mode 100644 index 0000000..53fc344 --- /dev/null +++ b/annotation-file-utilities/tests/NewInAnnotatedVariable.goal
@@ -0,0 +1,16 @@ +import java.lang.annotation.*; + +@Target(ElementType.TYPE_USE) +@interface Nullable {} +@Target(ElementType.TYPE_USE) +@interface NonNull {} + +@skip-test +public class NewInAnnotatedVariable { + Number b1 = new @NonNull Integer(0); + @NonNull Object b2 = new /*@Nullable*/ @NonNull Double(1); + @NonNull Runnable b3 = new /*@NonNull*/ Thread(); + @NonNull + ThreadLocal @NonNull [] b4 = new @NonNull InheritableThreadLocal @NonNull [3]; +} +
diff --git a/annotation-file-utilities/tests/NewInAnnotatedVariable.jaif b/annotation-file-utilities/tests/NewInAnnotatedVariable.jaif new file mode 100644 index 0000000..49ae8de --- /dev/null +++ b/annotation-file-utilities/tests/NewInAnnotatedVariable.jaif
@@ -0,0 +1,39 @@ +package: +annotation @NonNull: + +package: +class NewInAnnotatedVariable: +field b1: +new *0: @NonNull + +package: +class NewInAnnotatedVariable: +field b2: +new *0: @NonNull + +package: +class NewInAnnotatedVariable: +field b3: +new *0: @NonNull + +package : +class NewInAnnotatedVariable: +field b4: +type: @NonNull + +package : +class NewInAnnotatedVariable: +field b4: +type: +inner-type 0, 0: @NonNull + +package : +class NewInAnnotatedVariable: +field b4: +new *0: @NonNull + +package : +class NewInAnnotatedVariable: +field b4: +new *0: +inner-type 0, 0: @NonNull
diff --git a/annotation-file-utilities/tests/NewInAnnotatedVariable.java b/annotation-file-utilities/tests/NewInAnnotatedVariable.java new file mode 100644 index 0000000..971f5b0 --- /dev/null +++ b/annotation-file-utilities/tests/NewInAnnotatedVariable.java
@@ -0,0 +1,14 @@ +import java.lang.annotation.*; + +@Target(ElementType.TYPE_USE) +@interface Nullable {} +@Target(ElementType.TYPE_USE) +@interface NonNull {} + +public class NewInAnnotatedVariable { + Number b1 = new Integer(0); + @NonNull Object b2 = new /*@Nullable*/ Double(1); + @NonNull Runnable b3 = new /*@NonNull*/ Thread(); + ThreadLocal[] b4 = new InheritableThreadLocal[3]; +} +
diff --git a/annotation-file-utilities/tests/NewMultiple.goal b/annotation-file-utilities/tests/NewMultiple.goal new file mode 100644 index 0000000..45f9cba --- /dev/null +++ b/annotation-file-utilities/tests/NewMultiple.goal
@@ -0,0 +1,15 @@ +package annotator.tests; + +import java.util.List; + +public class NewMultiple { + void foo(Object o) { + List var = (/* @Tainted*/ @java.lang.Tainted List) o; + System.out.println(var); + } + + void bar(Object o) { + List var = (/* @UnderInitialization*/ @java.lang.UnderInitialization List) o; + System.out.println(var); + } +}
diff --git a/annotation-file-utilities/tests/NewMultiple.jaif b/annotation-file-utilities/tests/NewMultiple.jaif new file mode 100644 index 0000000..524c8b5 --- /dev/null +++ b/annotation-file-utilities/tests/NewMultiple.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class NewMultiple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + typecast #1: @java.lang.Tainted + + method bar(Ljava/lang/Object;)V: + typecast #1: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/NewMultiple.java b/annotation-file-utilities/tests/NewMultiple.java new file mode 100644 index 0000000..437fe4e --- /dev/null +++ b/annotation-file-utilities/tests/NewMultiple.java
@@ -0,0 +1,15 @@ +package annotator.tests; + +import java.util.List; + +public class NewMultiple { + void foo(Object o) { + List var = (/* @Tainted*/ List) o; + System.out.println(var); + } + + void bar(Object o) { + List var = (/* @UnderInitialization*/ List) o; + System.out.println(var); + } +}
diff --git a/annotation-file-utilities/tests/NewPackage.goal b/annotation-file-utilities/tests/NewPackage.goal new file mode 100644 index 0000000..ea69dd9 --- /dev/null +++ b/annotation-file-utilities/tests/NewPackage.goal
@@ -0,0 +1,5 @@ +public class NewPackage { + public NewPackage() { + Object o = new java.util.@X LinkedList(); + } +}
diff --git a/annotation-file-utilities/tests/NewPackage.jaif b/annotation-file-utilities/tests/NewPackage.jaif new file mode 100644 index 0000000..0414db0 --- /dev/null +++ b/annotation-file-utilities/tests/NewPackage.jaif
@@ -0,0 +1,6 @@ +package: +annotation @X: + +class NewPackage: +method <init>()V: +new *0: @X
diff --git a/annotation-file-utilities/tests/NewPackage.java b/annotation-file-utilities/tests/NewPackage.java new file mode 100644 index 0000000..d976789 --- /dev/null +++ b/annotation-file-utilities/tests/NewPackage.java
@@ -0,0 +1,5 @@ +public class NewPackage { + public NewPackage() { + Object o = new java.util.LinkedList(); + } +}
diff --git a/annotation-file-utilities/tests/NewSimple.goal b/annotation-file-utilities/tests/NewSimple.goal new file mode 100644 index 0000000..8f3379c --- /dev/null +++ b/annotation-file-utilities/tests/NewSimple.goal
@@ -0,0 +1,8 @@ +package annotator.tests; + +public class NewSimple { + public void foo() { + Object o = new @java.lang.Tainted Object(); + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/NewSimple.jaif b/annotation-file-utilities/tests/NewSimple.jaif new file mode 100644 index 0000000..4e0af52 --- /dev/null +++ b/annotation-file-utilities/tests/NewSimple.jaif
@@ -0,0 +1,11 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class NewSimple: + + method <init>()V: + + method foo()V: + new #0: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/NewSimple.java b/annotation-file-utilities/tests/NewSimple.java new file mode 100644 index 0000000..a079934 --- /dev/null +++ b/annotation-file-utilities/tests/NewSimple.java
@@ -0,0 +1,8 @@ +package annotator.tests; + +public class NewSimple { + public void foo() { + Object o = new Object(); + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/NonClass.goal b/annotation-file-utilities/tests/NonClass.goal new file mode 100644 index 0000000..a849c77 --- /dev/null +++ b/annotation-file-utilities/tests/NonClass.goal
@@ -0,0 +1,13 @@ +public class NonClass { + @X + interface I extends @X Comparable<@X I> {} + interface A { void m(@X Object p); } + enum B { ONE; void m() { @X Object l; } } + @interface C { @X String value() default "Ha!"; } + enum E { + @V(3) + @V(2) D((@V(1) String) new String()); + E(String s) { a = new @V(0) A() { @Override public void m(Object p) {} }; } + final @X A a; + } +}
diff --git a/annotation-file-utilities/tests/NonClass.jaif b/annotation-file-utilities/tests/NonClass.jaif new file mode 100644 index 0000000..aaefc7c --- /dev/null +++ b/annotation-file-utilities/tests/NonClass.jaif
@@ -0,0 +1,50 @@ +package: + +annotation @X: + +class NonClass$A: +method m(Ljava/lang/Object;)V: +parameter 0: +type: @X + +class NonClass$B: +method m()V: +local l: +type: @X + +class NonClass$C: +method value()Ljava/lang/String;: +return: @X + +annotation @V: + int value + +class NonClass$E: +field D: +typecast *0: @V(1) + +package: +class NonClass$E: +field D: +new *0: @V(2) + +package: +class NonClass$E: +field D: +type: @V(3) + +package: +class NonClass$E: +field a: +type: @X + +package: +class NonClass$E: +method <init>(Ljava/lang/String;)V: +new *0: @V(0) + +package: +class NonClass$I: @X +implements 0: @X +inner-type 3, 0: @X +
diff --git a/annotation-file-utilities/tests/NonClass.java b/annotation-file-utilities/tests/NonClass.java new file mode 100644 index 0000000..8129bbe --- /dev/null +++ b/annotation-file-utilities/tests/NonClass.java
@@ -0,0 +1,11 @@ +public class NonClass { + interface I extends Comparable<I> {} + interface A { void m(Object p); } + enum B { ONE; void m() { Object l; } } + @interface C { String value() default "Ha!"; } + enum E { + D((String) new String()); + E(String s) { a = new A() { @Override public void m(Object p) {} }; } + final A a; + } +}
diff --git a/annotation-file-utilities/tests/NonStandardSpacing.goal b/annotation-file-utilities/tests/NonStandardSpacing.goal new file mode 100644 index 0000000..648d0de --- /dev/null +++ b/annotation-file-utilities/tests/NonStandardSpacing.goal
@@ -0,0 +1,45 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +public abstract class NonStandardSpacing { + + public void m1() { + int a = ((@Anno int) (1 + 2)) + 3; + int b = ((@Anno int) (1 + /* comment */ 2)) + 3; + int c = ((@Anno int) (1 + + 2)) + 3; + int d = ((@Anno int) (1 + // comment + 2)) + 3; + int e = ((@Anno int) (1 + /* comment + end */ 2)) + 3; + } + + public void m2(@Anno NonStandardSpacing this) // comment() + { + + } + + public void m3(@Anno NonStandardSpacing this) throws @AnnoField(1) Exception { + + } + + // () + public abstract void m4 (@Anno NonStandardSpacing this); + + // () + public abstract void //comment + m5(@Anno NonStandardSpacing this); + + // () + public abstract void /* comment + end */ m6(@Anno NonStandardSpacing this); + + // () + public abstract void m7 + (@Anno NonStandardSpacing this); +} + +@Target(ElementType.TYPE_USE) +@interface AnnoField { + int value(); +}
diff --git a/annotation-file-utilities/tests/NonStandardSpacing.jaif b/annotation-file-utilities/tests/NonStandardSpacing.jaif new file mode 100644 index 0000000..ef48761 --- /dev/null +++ b/annotation-file-utilities/tests/NonStandardSpacing.jaif
@@ -0,0 +1,28 @@ +package: + annotation @Anno: + + class NonStandardSpacing: + method m1()V: + insert-typecast Block.statement 0, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 1, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 2, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 3, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 4, Variable.initializer, Binary.leftOperand: @Anno int + + method m2()V: + receiver: @Anno + + method m3()V: + receiver: @Anno + + method m4()V: + receiver: @Anno + + method m5()V: + receiver: @Anno + + method m6()V: + receiver: @Anno + + method m7()V: + receiver: @Anno
diff --git a/annotation-file-utilities/tests/NonStandardSpacing.java b/annotation-file-utilities/tests/NonStandardSpacing.java new file mode 100644 index 0000000..896b8c4 --- /dev/null +++ b/annotation-file-utilities/tests/NonStandardSpacing.java
@@ -0,0 +1,45 @@ +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +public abstract class NonStandardSpacing { + + public void m1() { + int a = 1 + 2 + 3; + int b = 1 + /* comment */ 2 + 3; + int c = 1 + + 2 + 3; + int d = 1 + // comment + 2 + 3; + int e = 1 + /* comment + end */ 2 + 3; + } + + public void m2() // comment() + { + + } + + public void m3() throws @AnnoField(1) Exception { + + } + + // () + public abstract void m4 (); + + // () + public abstract void //comment + m5(); + + // () + public abstract void /* comment + end */ m6(); + + // () + public abstract void m7 + (); +} + +@Target(ElementType.TYPE_USE) +@interface AnnoField { + int value(); +}
diff --git a/annotation-file-utilities/tests/Package.goal b/annotation-file-utilities/tests/Package.goal new file mode 100644 index 0000000..8cd06d7 --- /dev/null +++ b/annotation-file-utilities/tests/Package.goal
@@ -0,0 +1,5 @@ +package pkg.name.here; + +class Package { + @A Object o; +}
diff --git a/annotation-file-utilities/tests/Package.jaif b/annotation-file-utilities/tests/Package.jaif new file mode 100644 index 0000000..b82af4f --- /dev/null +++ b/annotation-file-utilities/tests/Package.jaif
@@ -0,0 +1,7 @@ +package: + annotation @A: @Target(TYPE_USE) + +package pkg.name.here: + class Package: + field o: + type: @A
diff --git a/annotation-file-utilities/tests/Package.java b/annotation-file-utilities/tests/Package.java new file mode 100644 index 0000000..14fc757 --- /dev/null +++ b/annotation-file-utilities/tests/Package.java
@@ -0,0 +1,5 @@ +package pkg.name.here; + +class Package { + Object o; +}
diff --git a/annotation-file-utilities/tests/ParseType.goal b/annotation-file-utilities/tests/ParseType.goal new file mode 100644 index 0000000..4ced733 --- /dev/null +++ b/annotation-file-utilities/tests/ParseType.goal
@@ -0,0 +1,20 @@ +import java.util.Map; + +public class ParseType<E> { + void m(Object o) { + Object o0 = ((@A String) (o)); + Object o1 = ((@A Map<String, Integer>) (o)); + Object o2 = ((String @A []) (o)); + Object o3 = ((String[] @A []) (o)); + Object o4 = ((@A Map<String[], Integer>) (o)); + Object o5 = ((@A Map<String[][], Integer>) (o)); + Object o6 = ((@A Map<?, ?>) (o)); + Object o7 = ((@A Map<? extends String, ? super List<Integer>>) (o)); + Object o8 = ((@A Map<List<String>, List<String>[]>) (o)); + Object o9 = ((@A Map.Entry<String, Integer>) (o)); + Object o10 = ((Map.Entry<String, Integer> @A []) (o)); + Object o11 = ((ParseType<String>.Inner<Integer> @A []) (o)); + } + + public class Inner<K> {} +}
diff --git a/annotation-file-utilities/tests/ParseType.jaif b/annotation-file-utilities/tests/ParseType.jaif new file mode 100644 index 0000000..9c6a0d2 --- /dev/null +++ b/annotation-file-utilities/tests/ParseType.jaif
@@ -0,0 +1,19 @@ +package: + +annotation @A: + +class ParseType: + + method m(Ljava/lang/Object;)V: + insert-typecast Block.statement 0, Variable.initializer: @A String + insert-typecast Block.statement 1, Variable.initializer: @A Map<String, Integer> + insert-typecast Block.statement 2, Variable.initializer: @A String[] + insert-typecast Block.statement 3, Variable.initializer: @A String[][] + insert-typecast Block.statement 4, Variable.initializer: @A Map<String[], Integer> + insert-typecast Block.statement 5, Variable.initializer: @A Map<String[][], Integer> + insert-typecast Block.statement 6, Variable.initializer: @A Map<?, ?> + insert-typecast Block.statement 7, Variable.initializer: @A Map<? extends String, ? super List<Integer>> + insert-typecast Block.statement 8, Variable.initializer: @A Map<List<String>, List<String>[]> + insert-typecast Block.statement 9, Variable.initializer: @A Map.Entry<String, Integer> + insert-typecast Block.statement 10, Variable.initializer: @A Map.Entry<String, Integer>[] + insert-typecast Block.statement 11, Variable.initializer: @A ParseType<String>.Inner<Integer>[]
diff --git a/annotation-file-utilities/tests/ParseType.java b/annotation-file-utilities/tests/ParseType.java new file mode 100644 index 0000000..8e969b3 --- /dev/null +++ b/annotation-file-utilities/tests/ParseType.java
@@ -0,0 +1,20 @@ +import java.util.Map; + +public class ParseType<E> { + void m(Object o) { + Object o0 = o; + Object o1 = o; + Object o2 = o; + Object o3 = o; + Object o4 = o; + Object o5 = o; + Object o6 = o; + Object o7 = o; + Object o8 = o; + Object o9 = o; + Object o10 = o; + Object o11 = o; + } + + public class Inner<K> {} +}
diff --git a/annotation-file-utilities/tests/ReceiverWithThrows.goal b/annotation-file-utilities/tests/ReceiverWithThrows.goal new file mode 100644 index 0000000..07d5da8 --- /dev/null +++ b/annotation-file-utilities/tests/ReceiverWithThrows.goal
@@ -0,0 +1,13 @@ +package annotator.tests; + +public class ReceiverWithThrows { + /* @UnderInitialization ReceiverWithThrows this */ + public void foo(@java.lang.UnderInitialization ReceiverWithThrows this) { + + } + + /* @Tainted ReceiverWithThrows this */ + public void bar(@java.lang.Tainted ReceiverWithThrows this) throws Exception { + + } +}
diff --git a/annotation-file-utilities/tests/ReceiverWithThrows.jaif b/annotation-file-utilities/tests/ReceiverWithThrows.jaif new file mode 100644 index 0000000..11e79d6 --- /dev/null +++ b/annotation-file-utilities/tests/ReceiverWithThrows.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ReceiverWithThrows: + + method <init>()V: + + method foo()V: + receiver: @java.lang.UnderInitialization + + method bar()V: + receiver: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/ReceiverWithThrows.java b/annotation-file-utilities/tests/ReceiverWithThrows.java new file mode 100644 index 0000000..79fddc7 --- /dev/null +++ b/annotation-file-utilities/tests/ReceiverWithThrows.java
@@ -0,0 +1,13 @@ +package annotator.tests; + +public class ReceiverWithThrows { + /* @UnderInitialization ReceiverWithThrows this */ + public void foo() { + + } + + /* @Tainted ReceiverWithThrows this */ + public void bar() throws Exception { + + } +}
diff --git a/annotation-file-utilities/tests/Receivers.goal b/annotation-file-utilities/tests/Receivers.goal new file mode 100644 index 0000000..f3be74c --- /dev/null +++ b/annotation-file-utilities/tests/Receivers.goal
@@ -0,0 +1,123 @@ +package annotator.tests; + +import java.io.Closeable; +import java.io.IOException; + +public class Receivers { + public void m(@checkers.tainting.quals.Tainted Receivers this) {} + + public void spaces(@checkers.tainting.quals.Tainted Receivers this) {} + + public void m(@checkers.tainting.quals.Tainted Receivers this, int i) {} + + public void spaces(@checkers.tainting.quals.Tainted Receivers this, int i) {} + + public void m(@checkers.tainting.quals.Tainted Receivers this, @Anno() String s) {} +} + +class Receivers2 { + public void m(@checkers.tainting.quals.Tainted Receivers2 this) {} + + public void spaces(@checkers.tainting.quals.Tainted Receivers2 this) {} + + public void m(@checkers.tainting.quals.Tainted Receivers2 this, int i) {} + + public void spaces(@checkers.tainting.quals.Tainted Receivers2 this, int i) {} +} + +class Receivers3<K, V> { + public void m(@checkers.tainting.quals.Tainted Receivers3<@annotation.Inner(0) K, @annotation.Inner(1) V> this) {} + + public void m(@checkers.tainting.quals.Tainted Receivers3<@annotation.Inner(0) K, @annotation.Inner(1) V> this, int i) {} +} + +class Receivers4<K, V> { + public void m(@checkers.tainting.quals.Tainted Receivers4<K, V> this) {} + + public void m(@checkers.tainting.quals.Tainted Receivers4<K, V> this, int i) {} +} + +interface Receivers5 { + public void m(@checkers.tainting.quals.Tainted Receivers5 this); +} + +enum Receivers6 { + TEST; + public void m(@checkers.tainting.quals.Tainted Receivers6 this) {} +} + +class Receivers7<K extends Object, V> { + public void m(@checkers.tainting.quals.Tainted Receivers7<@annotation.Inner(0) K, @annotation.Inner(1) V> this) {} +} + +class Receivers8<K extends Object> { + public void m(@checkers.tainting.quals.Tainted Receivers8<K> this) {} +} + +class Receivers9 { + public void m(@checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C Receivers9 this) {} +} + +class Receivers10<K, V> { + public void m(@checkers.tainting.quals.Tainted Receivers10<@annotation.A K, @annotation.B V> this) {} + + public void m(@checkers.tainting.quals.Tainted Receivers10<@annotation.A K, @annotation.B V> this, Receivers10<K, V> other) {} +} + +@interface Anno {} + +// Test receiver insertion on inner class's default constructor. +final class ScriptBasedMapping { + private final class RawScriptBasedMapping { + public RawScriptBasedMapping(@checkers.inference.quals.VarAnnot(0) ScriptBasedMapping ScriptBasedMapping.this) { super(); } + } +} + +// Test receiver insertion before first parameter annotation. +interface GenericInterface<T extends Object> { + public T map(T toMap); +} +class GenericArray<Z extends Object> implements GenericInterface<String []> { + private Z z; + public void setZ(Z z) { + this.z = z; + } + public String [] map(@trusted.quals.Untrusted GenericArray<Z> this, @trusted.quals.Untrusted String @trusted.quals.Untrusted [] toMap) { + return toMap; + } +} +class GenericFields { + private GenericArray<String> genArray; +} + +// Test inner receiver insertion before first parameter annotation. +class Outer<T, S> { + class Inner<T2 extends T> { + private S s; + private T t; + + protected void initialize(@checkers.inference.quals.VarAnnot(21) Outer<T, S>.Inner<@checkers.inference.quals.VarAnnot(20) T2> this, S s, T t) { + this.s = s; + this.t = t; + } + + public Inner(S s, T t) { + initialize(s, t); + } + } +} + +// Test that parameters inside an anonymous class get annotated. +interface Interface { + String get(@trusted.quals.Untrusted Interface this, @trusted.quals.Untrusted String param); +} + +// Test for infinite loop bug. +class Closer<T> implements Closeable { + private final Closeable proxyProvider = System.out; + + @Override + public void close(@checkers.inference.quals.VarAnnot(5) Closer<@checkers.inference.quals.VarAnnot(6) T> this) throws IOException { + proxyProvider.close(); + } +}
diff --git a/annotation-file-utilities/tests/Receivers.jaif b/annotation-file-utilities/tests/Receivers.jaif new file mode 100644 index 0000000..354277e --- /dev/null +++ b/annotation-file-utilities/tests/Receivers.jaif
@@ -0,0 +1,140 @@ +package annotation: +annotation @A: +annotation @B: +annotation @C: +annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package checkers.tainting.quals: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package checkers.inference.quals: + annotation @VarAnnot: + int value + +package trusted.quals: + annotation @Untrusted: + annotation @Trusted: + annotation @PolyTrusted: + +package annotator.tests: +class Receivers: + + method m()V: + receiver: @Tainted + + method spaces()V: + receiver: @Tainted + + method m(I)V: + receiver: @Tainted + + method spaces(I)V: + receiver: @Tainted + + method m(Ljava/lang/String;)V: + receiver: @Tainted + +class Receivers2: + + method m()V: + receiver: @Tainted + + method spaces()V: + receiver: @Tainted + + method m(I)V: + receiver: @Tainted + + method spaces(I)V: + receiver: @Tainted + +class Receivers3: + + method m()V: + receiver: @Tainted + inner-type 3, 0: @Inner(0) + inner-type 3, 1: @Inner(1) + + method m(I)V: + receiver: @Tainted + inner-type 3, 0: @Inner(0) + inner-type 3, 1: @Inner(1) + +class Receivers4: + + method m()V: + receiver: @Tainted + + method m(I)V: + receiver: @Tainted + +class Receivers5: + + method m()V: + receiver: @Tainted + +class Receivers6: + + method m()V: + receiver: @Tainted + +class Receivers7: + + method m()V: + receiver: @Tainted + inner-type 3, 0: @Inner(0) + inner-type 3, 1: @Inner(1) + +class Receivers8: + + method m()V: + receiver: @Tainted + +class Receivers9: + + method m()V: + receiver: @Tainted @A @B @C + +class Receivers10: + + method m()V: + receiver: @Tainted + inner-type 3, 0: @annotation.A + inner-type 3, 1: @annotation.B + + method m(Lannotator/tests/Receivers10;)V: + receiver: @Tainted + inner-type 3, 0: @annotation.A + inner-type 3, 1: @annotation.B + +class ScriptBasedMapping$RawScriptBasedMapping: + +method <init>()V: +insert-annotation Method.parameter -1: @checkers.inference.quals.VarAnnot(0) + +class GenericArray: + +method map([Ljava/lang/String;)[Ljava/lang/String;: +insert-annotation Method.parameter 0, Variable.type: @trusted.quals.Untrusted +insert-annotation Method.parameter -1: @trusted.quals.Untrusted +insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @trusted.quals.Untrusted + +class Outer$Inner: + +method initialize(Ljava/lang/Object;Ljava/lang/Object;)V: +insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @checkers.inference.quals.VarAnnot(20) +insert-annotation Method.parameter -1: @checkers.inference.quals.VarAnnot(21) + +class Interface: + +method get(Ljava/lang/String;)Ljava/lang/String;: +insert-annotation Method.parameter -1: @trusted.quals.Untrusted +insert-annotation Method.parameter 0, Variable.type: @trusted.quals.Untrusted + +class Closer: + +method close()V: +insert-annotation Method.parameter -1: @checkers.inference.quals.VarAnnot(5) +insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @checkers.inference.quals.VarAnnot(6) +
diff --git a/annotation-file-utilities/tests/Receivers.java b/annotation-file-utilities/tests/Receivers.java new file mode 100644 index 0000000..adc89ab --- /dev/null +++ b/annotation-file-utilities/tests/Receivers.java
@@ -0,0 +1,122 @@ +package annotator.tests; + +import java.io.Closeable; +import java.io.IOException; + +public class Receivers { + public void m() {} + + public void spaces() {} + + public void m(int i) {} + + public void spaces(int i) {} + + public void m(@Anno() String s) {} +} + +class Receivers2 { + public void m(Receivers2 this) {} + + public void spaces(Receivers2 this) {} + + public void m(Receivers2 this, int i) {} + + public void spaces(Receivers2 this, int i) {} +} + +class Receivers3<K, V> { + public void m() {} + + public void m(int i) {} +} + +class Receivers4<K, V> { + public void m(Receivers4<K, V> this) {} + + public void m(Receivers4<K, V> this, int i) {} +} + +interface Receivers5 { + public void m(); +} + +enum Receivers6 { + TEST; + public void m() {} +} + +class Receivers7<K extends Object, V> { + public void m() {} +} + +class Receivers8<K extends Object> { + public void m(Receivers8<K> this) {} +} + +class Receivers9 { + public void m() {} +} + +class Receivers10<K, V> { + public void m(Receivers10<K, V> this) {} + + public void m(Receivers10<K, V> this, Receivers10<K, V> other) {} +} + +@interface Anno {} + +// Test receiver insertion on inner class's default constructor. +final class ScriptBasedMapping { + private final class RawScriptBasedMapping { + } +} + +// Test receiver insertion before first parameter annotation. +interface GenericInterface<T extends Object> { + public T map(T toMap); +} +class GenericArray<Z extends Object> implements GenericInterface<String []> { + private Z z; + public void setZ(Z z) { + this.z = z; + } + public String [] map(String [] toMap) { + return toMap; + } +} +class GenericFields { + private GenericArray<String> genArray; +} + +// Test inner receiver insertion before first parameter annotation. +class Outer<T, S> { + class Inner<T2 extends T> { + private S s; + private T t; + + protected void initialize(S s, T t) { + this.s = s; + this.t = t; + } + + public Inner(S s, T t) { + initialize(s, t); + } + } +} + +// Test that parameters inside an anonymous class get annotated. +interface Interface { + String get(String param); +} + +// Test for infinite loop bug. +class Closer<T> implements Closeable { + private final Closeable proxyProvider = System.out; + + @Override + public void close() throws IOException { + proxyProvider.close(); + } +}
diff --git a/annotation-file-utilities/tests/StringEscape.goal b/annotation-file-utilities/tests/StringEscape.goal new file mode 100644 index 0000000..3d2776f --- /dev/null +++ b/annotation-file-utilities/tests/StringEscape.goal
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class StringEscape { + public void foo(@Key(desc="PartialRegex(\"\\\")") String orig) { + } +} +
diff --git a/annotation-file-utilities/tests/StringEscape.jaif b/annotation-file-utilities/tests/StringEscape.jaif new file mode 100644 index 0000000..f66e4a5 --- /dev/null +++ b/annotation-file-utilities/tests/StringEscape.jaif
@@ -0,0 +1,11 @@ +package : +annotation @Key: + String desc + +package annotator.tests: +class StringEscape: + method foo(Ljava/lang/String;)V: + return: + parameter #0: + type: @Key(desc="PartialRegex(\"\\\")") +
diff --git a/annotation-file-utilities/tests/StringEscape.java b/annotation-file-utilities/tests/StringEscape.java new file mode 100644 index 0000000..d8dee21 --- /dev/null +++ b/annotation-file-utilities/tests/StringEscape.java
@@ -0,0 +1,7 @@ +package annotator.tests; + +public class StringEscape { + public void foo(String orig) { + } +} +
diff --git a/annotation-file-utilities/tests/TypeCastGeneric.goal b/annotation-file-utilities/tests/TypeCastGeneric.goal new file mode 100644 index 0000000..f027421 --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastGeneric.goal
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; + +public class TypeCastGeneric { + public void foo(Object o) { + List<Integer> i = (List<@java.lang.UnderInitialization Integer>) o; + System.out.println(i); + } +}
diff --git a/annotation-file-utilities/tests/TypeCastGeneric.jaif b/annotation-file-utilities/tests/TypeCastGeneric.jaif new file mode 100644 index 0000000..3e23b10 --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastGeneric.jaif
@@ -0,0 +1,12 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeCastGeneric: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + typecast #1: + inner-type 3, 0: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/TypeCastGeneric.java b/annotation-file-utilities/tests/TypeCastGeneric.java new file mode 100644 index 0000000..e88cd87 --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastGeneric.java
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; + +public class TypeCastGeneric { + public void foo(Object o) { + List<Integer> i = (List<Integer>) o; + System.out.println(i); + } +}
diff --git a/annotation-file-utilities/tests/TypeCastMultiple.goal b/annotation-file-utilities/tests/TypeCastMultiple.goal new file mode 100644 index 0000000..3517807 --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastMultiple.goal
@@ -0,0 +1,15 @@ +package annotator.tests; + +import java.util.List; + +public class TypeCastMultiple { + public void foo(Object o) { + List myList = (@java.lang.UnderInitialization List) o; + Integer i = (@java.lang.Tainted Integer) o; + String s = (String) ((@java.lang.Tainted CharSequence) o); + Object n = (@A String & @B Comparable<@C String> & @D CharSequence) null; + System.out.println(myList); + System.out.println(i); + } +} +
diff --git a/annotation-file-utilities/tests/TypeCastMultiple.jaif b/annotation-file-utilities/tests/TypeCastMultiple.jaif new file mode 100644 index 0000000..185d67b --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastMultiple.jaif
@@ -0,0 +1,25 @@ +package : +annotation @A: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) +annotation @B: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) +annotation @C: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) +annotation @D: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) + +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeCastMultiple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + typecast #1: @java.lang.UnderInitialization + typecast #6: @java.lang.Tainted + //typecast #11: @java.lang.Tainted // inserts in wrong place! + typecast *3: @java.lang.Tainted + typecast *4, 0: @A + typecast *4, 1: @B + inner-type 3, 0: @C + typecast *4, 2: @D +
diff --git a/annotation-file-utilities/tests/TypeCastMultiple.java b/annotation-file-utilities/tests/TypeCastMultiple.java new file mode 100644 index 0000000..b2ecdad --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastMultiple.java
@@ -0,0 +1,15 @@ +package annotator.tests; + +import java.util.List; + +public class TypeCastMultiple { + public void foo(Object o) { + List myList = (List) o; + Integer i = (Integer) o; + String s = (String) ((CharSequence) o); + Object n = (String & Comparable<String> & CharSequence) null; + System.out.println(myList); + System.out.println(i); + } +} +
diff --git a/annotation-file-utilities/tests/TypeCastSimple.goal b/annotation-file-utilities/tests/TypeCastSimple.goal new file mode 100644 index 0000000..0db4599 --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastSimple.goal
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; + +public class TypeCastSimple { + public void foo(Object o) { + List myList = (/* @UnderInitialization*/ @java.lang.UnderInitialization List) o; + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/TypeCastSimple.jaif b/annotation-file-utilities/tests/TypeCastSimple.jaif new file mode 100644 index 0000000..76be40b --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastSimple.jaif
@@ -0,0 +1,11 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeCastSimple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + typecast #1: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/TypeCastSimple.java b/annotation-file-utilities/tests/TypeCastSimple.java new file mode 100644 index 0000000..89db0fb --- /dev/null +++ b/annotation-file-utilities/tests/TypeCastSimple.java
@@ -0,0 +1,10 @@ +package annotator.tests; + +import java.util.List; + +public class TypeCastSimple { + public void foo(Object o) { + List myList = (/* @UnderInitialization*/ List) o; + System.out.println(o); + } +}
diff --git a/annotation-file-utilities/tests/TypeParamMethod.goal b/annotation-file-utilities/tests/TypeParamMethod.goal new file mode 100644 index 0000000..f838e16 --- /dev/null +++ b/annotation-file-utilities/tests/TypeParamMethod.goal
@@ -0,0 +1,24 @@ +package annotator.tests; + +import java.util.Date; + +public class TypeParamMethod { + + public <@java.lang.Tainted T> void foo(T t) { + System.out.println(t); + } + + public <@java.lang.Tainted T extends Date> void foo2(T t) { + System.out.println(t); + } + + public <T, @java.lang.Tainted U> void foo(T t, U u) { + System.out.println(t); + System.out.println(u); + } + + public <T extends Date, @java.lang.Tainted U extends Date> void foo2(T t, U u) { + System.out.println(t); + System.out.println(u); + } +}
diff --git a/annotation-file-utilities/tests/TypeParamMethod.jaif b/annotation-file-utilities/tests/TypeParamMethod.jaif new file mode 100644 index 0000000..70e10d5 --- /dev/null +++ b/annotation-file-utilities/tests/TypeParamMethod.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeParamMethod: + + method foo(Ljava/lang/Object;)V: + typeparam 0: @java.lang.Tainted + + method foo2(Lannotator/tests/Date;)V: + typeparam 0: @java.lang.Tainted + + method foo(Ljava/lang/Object;Ljava/lang/Object;)V: + typeparam 1: @java.lang.Tainted + + method foo2(Ljava/util/Date;Ljava/util/Date;)V: + typeparam 1: @java.lang.Tainted
diff --git a/annotation-file-utilities/tests/TypeParamMethod.java b/annotation-file-utilities/tests/TypeParamMethod.java new file mode 100644 index 0000000..30a3fe2 --- /dev/null +++ b/annotation-file-utilities/tests/TypeParamMethod.java
@@ -0,0 +1,24 @@ +package annotator.tests; + +import java.util.Date; + +public class TypeParamMethod { + + public <T> void foo(T t) { + System.out.println(t); + } + + public <T extends Date> void foo2(T t) { + System.out.println(t); + } + + public <T, U> void foo(T t, U u) { + System.out.println(t); + System.out.println(u); + } + + public <T extends Date, U extends Date> void foo2(T t, U u) { + System.out.println(t); + System.out.println(u); + } +}
diff --git a/annotation-file-utilities/tests/Varargs.goal b/annotation-file-utilities/tests/Varargs.goal new file mode 100644 index 0000000..c6bf60f --- /dev/null +++ b/annotation-file-utilities/tests/Varargs.goal
@@ -0,0 +1,16 @@ +package annotator.tests; + +public class Varargs { + + public void m1(@java.lang.Nullable String ... params) { + } + + public void m2(String @java.lang.Nullable ... params) { + } + + public void m3(@java.lang.Nullable String @java.lang.Nullable ... params) { + } + + public void m4(@java.lang.Nullable Comparable<@java.lang.Nullable String> @java.lang.Nullable ... params) { + } +}
diff --git a/annotation-file-utilities/tests/Varargs.jaif b/annotation-file-utilities/tests/Varargs.jaif new file mode 100644 index 0000000..46e5f07 --- /dev/null +++ b/annotation-file-utilities/tests/Varargs.jaif
@@ -0,0 +1,26 @@ +package java.lang: + +// Note no @Target meta-annotation +annotation @Nullable: + + +package annotator.tests: + +class Varargs: + method m1([Ljava.lang.String;)V: + parameter #0: + type: + inner-type 0, 0: @Nullable + method m2([Ljava.lang.String;)V: + parameter #0: + type: @Nullable + method m3([Ljava.lang.String;)V: + parameter #0: + type: @Nullable + inner-type 0, 0: @Nullable + method m4([Ljava.lang.Comparable;)V: + parameter #0: + type: @Nullable + inner-type 0, 0: @Nullable + inner-type 0, 0, 3, 0: @Nullable +
diff --git a/annotation-file-utilities/tests/Varargs.java b/annotation-file-utilities/tests/Varargs.java new file mode 100644 index 0000000..b1ff4a4 --- /dev/null +++ b/annotation-file-utilities/tests/Varargs.java
@@ -0,0 +1,16 @@ +package annotator.tests; + +public class Varargs { + + public void m1(String ... params) { + } + + public void m2(String ... params) { + } + + public void m3(String ... params) { + } + + public void m4(Comparable<String> ... params) { + } +}
diff --git a/annotation-file-utilities/tests/VerifyDiffs.java b/annotation-file-utilities/tests/VerifyDiffs.java new file mode 100644 index 0000000..5d584e2 --- /dev/null +++ b/annotation-file-utilities/tests/VerifyDiffs.java
@@ -0,0 +1,92 @@ +import java.io.*; +import java.util.*; + +/** + * Little program that checks to see if every file ending in .diff in + * the current directory is empty. For each file, displays whether or not + * it is empty. If any file is non-empty, exits with an error exit status, + * else exits with a 0 exit status. + * + * Usage: java VerifyDiffs [--show_all] + * + * If --show_all option is used, all tests that pass will also be displayed. + * If --show_all is not used, and all tests pass, then there will be no + * output. + */ +public class VerifyDiffs { + private static boolean show_all = false; + + private static void parseArgs(String[] args) { + for (String s : args) { + if (s.equals("--show_all")) { + VerifyDiffs.show_all = true; + } + } + } + + public static void main(String[] args) { + parseArgs(args); + + int passCount = 0; + int failCount = 0; + boolean pass = true; + try { + + File dir = new File("."); + + List<File> allDiffs = new ArrayList<File>(); + gatherDiffs(allDiffs, dir); + Collections.sort(allDiffs); + for (File f : allDiffs) { + String fileName = f.toString(); + if (fileName.startsWith("./")) { + fileName = fileName.substring(2); + } + FileReader fr = new FileReader(f); + if (fr.read() != -1) { // if not empty, output error message + System.out.println(fileName + " ...FAILED"); + pass = false; + failCount++; + } else { + if (VerifyDiffs.show_all) { + System.out.println(fileName + " ...OK"); + } + passCount++; + } + fr.close(); + } + } catch (Exception e) { + System.out.println("verify diffs failed due to exception: " + + e.getMessage()); + pass = false; + } + + System.out.println("Passed: " + passCount + " Failed: " + failCount); + if (pass) { + if (VerifyDiffs.show_all) { + System.out.println("All tests succeeded."); + } + } else { + System.out.println("Tests failed."); + System.exit(1); + } + } + + /** + * Recursively adds all files in directory dir ending in .diff to + * the list diffs. + * + * @param diffs the array to place all diff files in + * @param dir the directory to start gathering diffs + */ + private static void gatherDiffs(List<File> diffs, File dir) { + for (File f : dir.listFiles()) { + if (f.toString().endsWith(".diff")) { + diffs.add(f); + } + if (f.isDirectory()) { + gatherDiffs(diffs, f); + } + } + } +}
diff --git a/annotation-file-utilities/tests/WildcardAnnoBound.goal b/annotation-file-utilities/tests/WildcardAnnoBound.goal new file mode 100644 index 0000000..d4c12a8 --- /dev/null +++ b/annotation-file-utilities/tests/WildcardAnnoBound.goal
@@ -0,0 +1,13 @@ +import java.util.List; +import java.util.Map; + +@interface Bla {} + +public class WildcardAnnoBound<X extends List<? extends @Bla Object>> { + WildcardAnnoBound(@Bla WildcardAnnoBound<X> n, X p) { + } +} + +class NoBound<X extends @A Object> {} + +class Bounds<@A X extends @B Object & @C Comparable<@D int @E []> & @F Map<@G ? extends @H Object, @I ?>, @J Y> {}
diff --git a/annotation-file-utilities/tests/WildcardAnnoBound.jaif b/annotation-file-utilities/tests/WildcardAnnoBound.jaif new file mode 100644 index 0000000..0517f2d --- /dev/null +++ b/annotation-file-utilities/tests/WildcardAnnoBound.jaif
@@ -0,0 +1,36 @@ +package: +annotation @Bla: +annotation @A: +annotation @B: +annotation @C: +annotation @D: +annotation @E: +annotation @F: +annotation @G: +annotation @H: +annotation @I: +annotation @J: + +class WildcardAnnoBound: +bound 0 & 0: +inner-type 3, 0, 2, 0: @Bla + +class WildcardAnnoBound: +method <init>(LWildcardAnnoBound;Ljava/util/List;)V: +parameter 0: +type: @Bla + +class NoBound: +bound 0 & 0: @A + +class Bounds: + typeparam 0: @A + typeparam 1: @J + bound 0 & 0: @B + bound 0 & 1: @C + inner-type 3, 0: @E + inner-type 3, 0, 0, 0: @D + bound 0 & 2: @F + inner-type 3, 0: @G + inner-type 3, 0, 2, 0: @H + inner-type 3, 1: @I
diff --git a/annotation-file-utilities/tests/WildcardAnnoBound.java b/annotation-file-utilities/tests/WildcardAnnoBound.java new file mode 100644 index 0000000..d4ce3d4 --- /dev/null +++ b/annotation-file-utilities/tests/WildcardAnnoBound.java
@@ -0,0 +1,13 @@ +import java.util.List; +import java.util.Map; + +@interface Bla {} + +public class WildcardAnnoBound<X extends List<? extends Object>> { + WildcardAnnoBound(WildcardAnnoBound<X> n, X p) { + } +} + +class NoBound<X> {} + +class Bounds<X extends Object & Comparable<int[]> & Map<? extends Object, ?>, Y> {}
diff --git a/annotation-file-utilities/tests/abbreviated/C.jaif b/annotation-file-utilities/tests/abbreviated/C.jaif new file mode 100644 index 0000000..47c586f --- /dev/null +++ b/annotation-file-utilities/tests/abbreviated/C.jaif
@@ -0,0 +1,17 @@ +package javax.annotation: +annotation @Resource: @Target(value={TYPE,FIELD,METHOD}) + +package org.checkerframework.checker.nullness.qual: +annotation @Nullable: + +package : +class C1: + + field values: + type: @javax.annotation.Resource + + method foo(Ljava/lang/Object;)Lplume/C1;: + return: + parameter #0: + type: @org.checkerframework.checker.nullness.qual.Nullable +
diff --git a/annotation-file-utilities/tests/abbreviated/C0.goal b/annotation-file-utilities/tests/abbreviated/C0.goal new file mode 100644 index 0000000..5055295 --- /dev/null +++ b/annotation-file-utilities/tests/abbreviated/C0.goal
@@ -0,0 +1 @@ +public class C0 {}
diff --git a/annotation-file-utilities/tests/abbreviated/C0.java b/annotation-file-utilities/tests/abbreviated/C0.java new file mode 100644 index 0000000..5055295 --- /dev/null +++ b/annotation-file-utilities/tests/abbreviated/C0.java
@@ -0,0 +1 @@ +public class C0 {}
diff --git a/annotation-file-utilities/tests/abbreviated/C1.goal b/annotation-file-utilities/tests/abbreviated/C1.goal new file mode 100644 index 0000000..95aeffe --- /dev/null +++ b/annotation-file-utilities/tests/abbreviated/C1.goal
@@ -0,0 +1,9 @@ +import org.checkerframework.checker.nullness.qual.Nullable; +import javax.annotation.Resource; +public class C1<T extends Object> { + @Resource + int values = 0; + public <A extends Object> C1<A> foo(@Nullable A a) { + return null; + } +}
diff --git a/annotation-file-utilities/tests/abbreviated/C1.java b/annotation-file-utilities/tests/abbreviated/C1.java new file mode 100644 index 0000000..4df3603 --- /dev/null +++ b/annotation-file-utilities/tests/abbreviated/C1.java
@@ -0,0 +1,7 @@ +import javax.annotation.Resource; +public class C1<T extends Object> { + int values = 0; + public <A extends Object> C1<A> foo(A a) { + return null; + } +}
diff --git a/annotation-file-utilities/tests/abbreviated/Makefile b/annotation-file-utilities/tests/abbreviated/Makefile new file mode 100644 index 0000000..368c0a2 --- /dev/null +++ b/annotation-file-utilities/tests/abbreviated/Makefile
@@ -0,0 +1,85 @@ +# Very rough testing framework for the annotator. Running 'make all' will +# look for all myClass.goal files in this directory, run the annotator on the +# corresponding .jaif and .java files, and then output the difference in a +# myClass.diff file in this directory. +# +# To test just one file, use (for example) 'make myClass.diff'. + +# Put user-specific changes in your own Makefile.user. +# Make will silently continue if that file does not exist. +-include ../Makefile.user + +# Override these in Makefile.user if the java and javac commands are not on +# your execution path. Example from Makefile.user: +# JAVA=${JAVA_HOME}/bin/java +# JAVAC=${JAVA_HOME}/bin/javac +JAVA?=java +JAVAC?=javac + +export SHELL=/bin/bash -o pipefail + + +DIFFS := $(wildcard *.goal) +DISABLED := $(shell grep -le "@skip-test" $(DIFFS)) +FILTERED := $(filter-out $(DISABLED),$(DIFFS)) +DIFFS := $(patsubst %.goal, %.diff, $(FILTERED)) +AFU_JARS := ../../lib/plume-core.jar ../../annotation-file-utilities.jar +JAIF := C.jaif +SRC := $(patsubst %.goal, %.java, $(FILTERED)) +OUT := $(patsubst %.goal, %.output, $(FILTERED)) + +DEBUG := +# Use this to enable some debugging. +# DEBUG := --debug + +default : all + +.PHONY: all +all : $(DIFFS) results + +# Display results of all .diff files. +.PHONY: results +results: ../bin/VerifyDiffs.class + @rm -rf output + @echo "" + @echo "=== RESULTS ===" + @echo "" + @$(JAVA) -cp bin:../bin VerifyDiffs --show_all + +# Remakes the little java program that checks and compares diffs +../bin/VerifyDiffs.class : ../VerifyDiffs.java + @$(JAVAC) -g -cp ../../bincompile -d ../bin ../VerifyDiffs.java + +# Compiles all the test cases (be verbose about this). +.PHONY: compile +compile : $(SRC) + mkdir -p bin + $(JAVAC) -g -cp bin:../../bin -d bin -sourcepath . $(SRC) + +# Actually runs the annotator to create the annotated java file. +output: compile $(JAIF) ../../bin $(AFU_JARS) + $(JAVA) \ + -cp ../../bin:../../annotation-file-utilities.jar:bin \ + annotator.Main \ + ${DEBUG} \ + --abbreviate=true \ + -d output \ + $(JAIF) \ + $(SRC) \ + 2>&1 | tee C.log + +.PRECIOUS: %.output +%.output: output + find output -name "$*.java" -print | xargs cat > "$*.output" + +# Compare the output of the annotator and the goal file. +%.diff: %.goal %.output + -diff -u $*.goal $*.output >& $*.diff + +# Remove all .diff, .log files from the tests directory. +.PHONY: clean +clean : + rm -rf bin + rm -f *.diff + rm -f *.log + rm -f *.output
diff --git a/annotation-file-utilities/tests/abbreviated/README.md b/annotation-file-utilities/tests/abbreviated/README.md new file mode 100644 index 0000000..f870261 --- /dev/null +++ b/annotation-file-utilities/tests/abbreviated/README.md
@@ -0,0 +1,6 @@ +For motivation/explanation, see +[Issue 111](https://github.com/typetools/annotation-tools/issues/111). +(Note that `C2` in the linked issue became `C0` here because the outcome +depends on the order of the arguments, and `Makefile` passes the files +to the annotator in reverse lexicographical order.) +
diff --git a/annotation-file-utilities/tests/ad-hoc/Makefile b/annotation-file-utilities/tests/ad-hoc/Makefile new file mode 100644 index 0000000..e372c78 --- /dev/null +++ b/annotation-file-utilities/tests/ad-hoc/Makefile
@@ -0,0 +1,10 @@ +.PHONY: all +all: bridge + +.PHONY: bridge +bridge: + ${MAKE} -C bridge + +.PHONY: clean +clean: + ${MAKE} -C bridge clean
diff --git a/annotation-file-utilities/tests/ad-hoc/README b/annotation-file-utilities/tests/ad-hoc/README new file mode 100644 index 0000000..6ba5d8e --- /dev/null +++ b/annotation-file-utilities/tests/ad-hoc/README
@@ -0,0 +1 @@ +This directory is a place to put tests that don't fit into the harness.
diff --git a/annotation-file-utilities/tests/ad-hoc/bridge/C.java b/annotation-file-utilities/tests/ad-hoc/bridge/C.java new file mode 100644 index 0000000..3bf6155 --- /dev/null +++ b/annotation-file-utilities/tests/ad-hoc/bridge/C.java
@@ -0,0 +1,11 @@ +@interface A {} + +class C implements Comparable<C> { + String value = ""; + + @A + public int compareTo(C other) { + return value.compareTo(other.value); + } +} +
diff --git a/annotation-file-utilities/tests/ad-hoc/bridge/Makefile b/annotation-file-utilities/tests/ad-hoc/bridge/Makefile new file mode 100644 index 0000000..e85d0f7 --- /dev/null +++ b/annotation-file-utilities/tests/ad-hoc/bridge/Makefile
@@ -0,0 +1,31 @@ +.PHONY: all +all: bridge no-bridge + +JAVAC?=javac + +JAVACVERSION = $(shell $(JAVAC) -version 2>&1) +ifeq (1.7.,$(findstring 1.7.,$(JAVACVERSION))) + JAVACTARGET = -source 7 -target 7 +endif + +.PHONY: bridge +# Counting bridge methods, there should be 3 @A annotations. +bridge: C.class + CLASSPATH=.:${CLASSPATH} ../../../scripts/extract-annotations C.class +# skip test if using javac 1.7, which doesn't copy annotations to bridge methods + test 3 -eq `grep -c -w '@A' C.jaif` || (echo "Didn't find 3 '@A' in C.jaif:" && cat C.jaif && false) + +.PHONY: no-bridge +# Not counting bridge methods, there should be 2 @A annotations. +# -b ignores annotations on bridge methods +no-bridge: C.class + CLASSPATH=.:${CLASSPATH} ../../../scripts/extract-annotations -b C.class + test 2 -eq `grep -c -w '@A' C.jaif` || (echo "Didn't find 2 '@A' in C.jaif:" && cat C.jaif && false) + +C.class: C.java + $(XJAVAC) -g $(JAVACTARGET) C.java + +.PHONY: clean +clean: + rm -rf *.class C.jaif annotated out +
diff --git a/annotation-file-utilities/tests/ad-hoc/bridge/README b/annotation-file-utilities/tests/ad-hoc/bridge/README new file mode 100644 index 0000000..3854ea1 --- /dev/null +++ b/annotation-file-utilities/tests/ad-hoc/bridge/README
@@ -0,0 +1,5 @@ +Test of bridge method inclusion/exclusion. For the default target +(created with "make all"), the extracted JAIF should contain entries for +both "compareTo(LC;)I" and "compareTo(Ljava/lang/Object;)I"; for the +alternate target ("make alt"), the JAIF should omit the latter. +
diff --git a/annotation-file-utilities/tests/converted/ASTInsert.jaif b/annotation-file-utilities/tests/converted/ASTInsert.jaif new file mode 100644 index 0000000..7489db6 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ASTInsert.jaif
@@ -0,0 +1,127 @@ +package checkers.nullness.quals: +annotation @Nullable: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME) + +package : +annotation @A: + +package : +annotation @B: + +package : +annotation @C: + +package checkers.nullness.quals: +annotation @NonNull: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME) + +package : +annotation @Bla: + +package : +annotation @D: + +package : +annotation @H: + +package : +annotation @E: + +package : +annotation @G: + +package : +annotation @I: + +package : +annotation @F: + +package : +annotation @J: + +package : +class ASTInsert: + + field c: + insert-annotation Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable + insert-annotation Variable.initializer, Binary.rightOperand: @checkers.nullness.quals.Nullable + insert-annotation Variable.initializer: @checkers.nullness.quals.Nullable + + field str: + insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable + insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable + + field sa: + insert-annotation Variable.type, ArrayType.type: @Bla + insert-annotation Variable.initializer, NewArray.type 0: @Bla + inner-type 0, 0: @Bla + insert-annotation Variable.type: @Bla + + method m(Ljava/lang/String;[Ljava/lang/String;I)I: + return: + insert-annotation Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 16, Variable.initializer, MemberSelect.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 14, Variable.initializer, InstanceOf.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 18, Variable.initializer, NewArray.dimension 1: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 7, Switch.expression, Parenthesized.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 21, If.thenStatement, Return.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 23, If.thenStatement, Throw.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 12, ForLoop.initializer 1, Variable.initializer: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 12, ForLoop.condition, Binary.rightOperand: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable + insert-annotation Block.statement 30, Variable.initializer: @checkers.nullness.quals.Nullable @A @B @C + + method context()V: + return: + insert-annotation Block.statement 0, ExpressionStatement.expression, MethodInvocation.typeArgument 0: @checkers.nullness.quals.NonNull + +package : +class Wild: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0, ParameterizedType.typeArgument 0, TypeParameter.bound 0: @Bla + + method <init>(LWild;Ljava/util/List;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @Bla + +package : +class Unbound: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @A + +package : +class Bound: + insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0, ArrayType.type: @D + insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0, Wildcard.bound: @H + insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0: @E + insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0: @G + insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 1: @I + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @B + insert-annotation Class.typeParameter 0, TypeParameter.bound 1: @C + insert-annotation Class.typeParameter 0, TypeParameter.bound 2: @F + insert-annotation Class.typeParameter 0: @A + insert-annotation Class.typeParameter 1: @J +
diff --git a/annotation-file-utilities/tests/converted/AnonInner.jaif b/annotation-file-utilities/tests/converted/AnonInner.jaif new file mode 100644 index 0000000..bfbbe89 --- /dev/null +++ b/annotation-file-utilities/tests/converted/AnonInner.jaif
@@ -0,0 +1,45 @@ +package : +annotation @A: + +package : +annotation @D: + +package : +annotation @C: + +package : +annotation @B: + +package : +annotation @F: + +package : +annotation @E: + +package : +class AnonInner$InnerOne: + + method m1(Ljava/lang/String;)Ljava/lang/Object;: + return: + insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @A + + method m2(Ljava/lang/String;)Ljava/lang/Object;: + return: + insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @D + +package : +class AnonInner$InnerOne$1: + + method e1(Ljava/lang/Object;)Ljava/lang/Object;: + return: + insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @C + insert-annotation Method.type: @B + +package : +class AnonInner$InnerOne$2: + + method e2(Ljava/lang/Object;)Ljava/lang/Object;: + return: + insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @F + insert-annotation Method.type: @E +
diff --git a/annotation-file-utilities/tests/converted/ArrayMultiDim.jaif b/annotation-file-utilities/tests/converted/ArrayMultiDim.jaif new file mode 100644 index 0000000..6279f96 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ArrayMultiDim.jaif
@@ -0,0 +1,37 @@ +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @F: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ArrayMultiDim: + + field field: + insert-annotation Variable.type, ArrayType.type, ArrayType.type, ArrayType.type: @java.lang.C + insert-annotation Variable.type, ArrayType.type, ArrayType.type: @java.lang.F + insert-annotation Variable.type, ArrayType.type: @java.lang.E + insert-annotation Variable.type: @java.lang.D + + field field2: + insert-annotation Variable.initializer, NewArray.type 0: @java.lang.D + insert-annotation Variable.initializer, NewArray.type 1: @java.lang.E + insert-annotation Variable.initializer, NewArray.type 2: @java.lang.F + insert-annotation Variable.initializer, NewArray.type 3: @java.lang.C + + field field3: + insert-annotation Variable.type, ArrayType.type, ArrayType.type, ArrayType.type: @java.lang.C + insert-annotation Variable.type, ArrayType.type, ArrayType.type: @java.lang.F + insert-annotation Variable.type, ArrayType.type: @java.lang.E + insert-annotation Variable.initializer, NewArray.type 0: @java.lang.D + insert-annotation Variable.initializer, NewArray.type 1: @java.lang.E + insert-annotation Variable.initializer, NewArray.type 2: @java.lang.F + insert-annotation Variable.initializer, NewArray.type 3: @java.lang.C + insert-annotation Variable.type: @java.lang.D +
diff --git a/annotation-file-utilities/tests/converted/ArrayParamSimple.jaif b/annotation-file-utilities/tests/converted/ArrayParamSimple.jaif new file mode 100644 index 0000000..4c6a4aa --- /dev/null +++ b/annotation-file-utilities/tests/converted/ArrayParamSimple.jaif
@@ -0,0 +1,25 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Nullable: + +package annotator.tests: +class ArrayParamSimple: + + method m1([Ljava/lang/Integer;)V: + return: + insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Tainted + + method m2([Ljava/lang/Integer;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @java.lang.Tainted + + method m3([Ljava/lang/Integer;)V: + return: + insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable + + method m4([Ljava/lang/Integer;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable +
diff --git a/annotation-file-utilities/tests/converted/ArrayReturnTypeSimple.jaif b/annotation-file-utilities/tests/converted/ArrayReturnTypeSimple.jaif new file mode 100644 index 0000000..0ec9a39 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ArrayReturnTypeSimple.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ArrayReturnTypeSimple: + + method foo()[Ljava/lang/Object;: + return: + insert-annotation Method.type, ArrayType.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/BoundClassMultiple.jaif b/annotation-file-utilities/tests/converted/BoundClassMultiple.jaif new file mode 100644 index 0000000..9be7354 --- /dev/null +++ b/annotation-file-utilities/tests/converted/BoundClassMultiple.jaif
@@ -0,0 +1,23 @@ +package java.lang: +annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundClassMultiple: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @java.lang.A + insert-annotation Class.typeParameter 1, TypeParameter.bound 0: @java.lang.B + insert-annotation Class.typeParameter 2, TypeParameter.bound 0: @java.lang.D + insert-annotation Class.typeParameter 1, TypeParameter.bound 1: @java.lang.C + insert-annotation Class.typeParameter 2, TypeParameter.bound 0, ParameterizedType.typeArgument 0: @java.lang.E +
diff --git a/annotation-file-utilities/tests/converted/BoundClassSimple.jaif b/annotation-file-utilities/tests/converted/BoundClassSimple.jaif new file mode 100644 index 0000000..5bc0c73 --- /dev/null +++ b/annotation-file-utilities/tests/converted/BoundClassSimple.jaif
@@ -0,0 +1,7 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundClassSimple: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/BoundMethodMultiple.jaif b/annotation-file-utilities/tests/converted/BoundMethodMultiple.jaif new file mode 100644 index 0000000..26e0281 --- /dev/null +++ b/annotation-file-utilities/tests/converted/BoundMethodMultiple.jaif
@@ -0,0 +1,39 @@ +package java.lang: +annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @F: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundMethodMultiple: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @java.lang.A + + method foo(Ljava/util/Date;)V: + return: + insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.B + + method foo(Ljava/util/List;)V: + return: + insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.C + insert-annotation Method.typeParameter 0, TypeParameter.bound 1: @java.lang.D + + method foo(Ljava/util/Date;Ljava/util/Map;)V: + return: + insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.E + insert-annotation Method.typeParameter 1, TypeParameter.bound 0: @java.lang.F +
diff --git a/annotation-file-utilities/tests/converted/BoundMethodSimple.jaif b/annotation-file-utilities/tests/converted/BoundMethodSimple.jaif new file mode 100644 index 0000000..fb2a8ca --- /dev/null +++ b/annotation-file-utilities/tests/converted/BoundMethodSimple.jaif
@@ -0,0 +1,14 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class BoundMethodSimple: + + method foo(Ljava/util/Date;)V: + return: + insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.Tainted + + method foo2(Ljava/lang/Object;)V: + return: + insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/CastInsert.jaif b/annotation-file-utilities/tests/converted/CastInsert.jaif new file mode 100644 index 0000000..2863e2a --- /dev/null +++ b/annotation-file-utilities/tests/converted/CastInsert.jaif
@@ -0,0 +1,84 @@ +package checkers.nullness.quals: +annotation @Nullable: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME) + +package annotation: +annotation @A: + +package annotation: +annotation @B: + +package annotation: +annotation @C: + +package : +class CastInsert: + + field c: + insert-typecast Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Variable.initializer, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Variable.initializer: @checkers.nullness.quals.Nullable Integer + + field str: + insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable String + insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable String + + method m(Ljava/lang/String;[Ljava/lang/String;I)I: + return: + insert-typecast Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @checkers.nullness.quals.Nullable CastInsert + insert-typecast Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Boolean + insert-typecast Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable PrintStream + insert-typecast Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable String + insert-typecast Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable String + insert-typecast Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable String + insert-typecast Block.statement 16, Variable.initializer, MemberSelect.expression: @checkers.nullness.quals.Nullable CastInsert + insert-typecast Block.statement 14, Variable.initializer, InstanceOf.expression: @checkers.nullness.quals.Nullable String + insert-typecast Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 18, Variable.initializer, NewArray.dimension 1: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @checkers.nullness.quals.Nullable String + insert-typecast Block.statement 7, Switch.expression, Parenthesized.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 21, If.thenStatement, Return.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 23, If.thenStatement, Throw.expression: @checkers.nullness.quals.Nullable RuntimeException + insert-typecast Block.statement 12, ForLoop.initializer 1, Variable.initializer: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 12, ForLoop.condition, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @checkers.nullness.quals.Nullable Integer + insert-typecast Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable String + insert-typecast Block.statement 30, Variable.initializer: @checkers.nullness.quals.Nullable @annotation.A @annotation.B @annotation.C String + + method m2(Ljava/lang/Object;)V: + return: + insert-typecast Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Map<String, Integer> + inner-type 3, 0: @annotation.A + inner-type 3, 1: @annotation.B + insert-typecast Block.statement 1, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Object[][] + inner-type 0, 0: @annotation.A + inner-type 0, 0,0, 0: @annotation.B + insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable int[][] + inner-type 0, 0: @annotation.A + inner-type 0, 0,0, 0: @annotation.B + insert-typecast Block.statement 3, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable List<? extends Object> + inner-type 3, 0: @annotation.A + inner-type 3, 0,2, 0: @annotation.B + insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression: int + insert-typecast Block.statement 5, ExpressionStatement.expression, Assignment.expression: Map<String, Integer> + inner-type 3, 0: @annotation.A + inner-type 3, 1: @annotation.B +
diff --git a/annotation-file-utilities/tests/converted/ClassAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/ClassAnnotationParameter.jaif new file mode 100644 index 0000000..f85e429 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ClassAnnotationParameter.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @TestAnnotation: + Class value + +package annotator.tests: +class ClassAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value=java.lang.Object.class) + return: +
diff --git a/annotation-file-utilities/tests/converted/ClassListAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/ClassListAnnotationParameter.jaif new file mode 100644 index 0000000..e74fcba --- /dev/null +++ b/annotation-file-utilities/tests/converted/ClassListAnnotationParameter.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @TestAnnotation: + Class[] value + +package annotator.tests: +class ClassListAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value={java.lang.Object.class,java.lang.String.class}) + return: +
diff --git a/annotation-file-utilities/tests/converted/ClassSimple.jaif b/annotation-file-utilities/tests/converted/ClassSimple.jaif new file mode 100644 index 0000000..b657991 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ClassSimple.jaif
@@ -0,0 +1,6 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ClassSimple: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/ComplexLocationOne.jaif b/annotation-file-utilities/tests/converted/ComplexLocationOne.jaif new file mode 100644 index 0000000..96e299d --- /dev/null +++ b/annotation-file-utilities/tests/converted/ComplexLocationOne.jaif
@@ -0,0 +1,32 @@ +package java.lang: +annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ComplexLocationOne: + + field field: + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 1, ArrayType.type: @java.lang.D + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.C + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 1: @java.lang.E + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.B + insert-annotation Variable.type: @java.lang.A + + field entries: + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type, MemberSelect.expression, ParameterizedType.typeArgument 1, ArrayType.type: @java.lang.B + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type, MemberSelect.expression, ParameterizedType.typeArgument 0: @java.lang.A + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 1, ArrayType.type: @java.lang.E + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type: @java.lang.C + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.D +
diff --git a/annotation-file-utilities/tests/converted/ComplexLocationTwo.jaif b/annotation-file-utilities/tests/converted/ComplexLocationTwo.jaif new file mode 100644 index 0000000..0ab8111 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ComplexLocationTwo.jaif
@@ -0,0 +1,37 @@ +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @F: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @H: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @G: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ComplexLocationTwo: + + field field: + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ArrayType.type, ArrayType.type, ArrayType.type: @java.lang.C + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ArrayType.type, ArrayType.type: @java.lang.F + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ArrayType.type: @java.lang.E + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.D + insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @java.lang.H + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.B + insert-annotation Variable.type, ParameterizedType.typeArgument 1: @java.lang.G + insert-annotation Variable.type: @java.lang.A +
diff --git a/annotation-file-utilities/tests/converted/ConstructorParam.jaif b/annotation-file-utilities/tests/converted/ConstructorParam.jaif new file mode 100644 index 0000000..172bcee --- /dev/null +++ b/annotation-file-utilities/tests/converted/ConstructorParam.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ConstructorParam: + + method <init>(I)V: + return: + insert-annotation Method.parameter 0, Variable.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/ConstructorParamMultiple.jaif b/annotation-file-utilities/tests/converted/ConstructorParamMultiple.jaif new file mode 100644 index 0000000..c4729b3 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ConstructorParamMultiple.jaif
@@ -0,0 +1,16 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ConstructorParamMultiple: + + method <init>(Ljava/lang/Object;Ljava/util/List;I)V: + return: + insert-annotation Method.parameter 1, Variable.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization + insert-annotation Method.parameter 0, Variable.type: @java.lang.Tainted + insert-annotation Method.parameter 1, Variable.type: @java.lang.Tainted + insert-annotation Method.parameter 2, Variable.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/ConstructorReceivers.jaif b/annotation-file-utilities/tests/converted/ConstructorReceivers.jaif new file mode 100644 index 0000000..21a8b82 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ConstructorReceivers.jaif
@@ -0,0 +1,122 @@ +package checkers.taiting.quals: +annotation @Tainted: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME) + +package annotation: +annotation @A: + +package annotation: +annotation @B: + +package annotation: +annotation @C: + +package annotation: +annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package annotator.tests: +class ConstructorReceivers$C0: + + method <init>()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method <init>(I)V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class ConstructorReceivers$C1: + + method <init>()V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + + method <init>(I)V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + +package annotator.tests: +class ConstructorReceivers$C2: + + method <init>()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C + +package annotator.tests: +class ConstructorReceivers$C3: + + method <init>()V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C + +package annotator.tests: +class ConstructorReceivers$P0$C4: + + method <init>()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method <init>(I)V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class ConstructorReceivers$P0$C5: + + method <init>()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted + + method <init>(I)V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted + +package annotator.tests: +class ConstructorReceivers$P0$C6: + + method <init>()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method <init>(Lannotator/tests/ConstructorReceivers$P0;)V: + return: + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class ConstructorReceivers$P0$C7: + + method <init>()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted + + method <init>(Lannotator/tests/ConstructorReceivers$P0;)V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + +package annotator.tests: +class ConstructorReceivers$P1$C8: + + method <init>()V: + return: + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class ConstructorReceivers$P1$C9: + + method <init>()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) +
diff --git a/annotation-file-utilities/tests/converted/ConstructorReturn.jaif b/annotation-file-utilities/tests/converted/ConstructorReturn.jaif new file mode 100644 index 0000000..3d8f45d --- /dev/null +++ b/annotation-file-utilities/tests/converted/ConstructorReturn.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ConstructorReturn: + + method <init>()V: + return: + insert-annotation Method.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/Date.jaif b/annotation-file-utilities/tests/converted/Date.jaif new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/annotation-file-utilities/tests/converted/Date.jaif
diff --git a/annotation-file-utilities/tests/converted/DeclarationAnnotation.jaif b/annotation-file-utilities/tests/converted/DeclarationAnnotation.jaif new file mode 100644 index 0000000..1f84e70 --- /dev/null +++ b/annotation-file-utilities/tests/converted/DeclarationAnnotation.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @SuppressWarnings: + String value + +package annotator.tests: +class DeclarationAnnotation: + + method foo()V: @java.lang.SuppressWarnings(value="string argument") + return: +
diff --git a/annotation-file-utilities/tests/converted/DuplicateAnnotation.jaif b/annotation-file-utilities/tests/converted/DuplicateAnnotation.jaif new file mode 100644 index 0000000..b3f7141 --- /dev/null +++ b/annotation-file-utilities/tests/converted/DuplicateAnnotation.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @SuppressWarnings: + String value + +package java.lang: +annotation @Deprecated: + +package annotator.tests: +class DuplicateAnnotation: + + method m5()V: @java.lang.SuppressWarnings(value="E") + return: + + method m6()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Deprecated +
diff --git a/annotation-file-utilities/tests/converted/EnumAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/EnumAnnotationParameter.jaif new file mode 100644 index 0000000..e1daf89 --- /dev/null +++ b/annotation-file-utilities/tests/converted/EnumAnnotationParameter.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @TestAnnotation: + enum java.lang.TestEnum value + +package annotator.tests: +class EnumAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value=A) + return: +
diff --git a/annotation-file-utilities/tests/converted/EnumListAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/EnumListAnnotationParameter.jaif new file mode 100644 index 0000000..1192c40 --- /dev/null +++ b/annotation-file-utilities/tests/converted/EnumListAnnotationParameter.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @TestAnnotation: + enum java.lang.TestEnum[] value + +package annotator.tests: +class EnumListAnnotationParameter: + + method foo()V: @java.lang.TestAnnotation(value={A,B}) + return: +
diff --git a/annotation-file-utilities/tests/converted/ExtImpl.jaif b/annotation-file-utilities/tests/converted/ExtImpl.jaif new file mode 100644 index 0000000..c0dd1ca --- /dev/null +++ b/annotation-file-utilities/tests/converted/ExtImpl.jaif
@@ -0,0 +1,36 @@ +package : +annotation @B: + +package : +annotation @E: + +package : +annotation @C: + +package : +annotation @F: + +package : +annotation @A: + +package : +annotation @D: + +package : +class ExtImpl$C1: + insert-annotation Class.bound -1, ParameterizedType.typeArgument 0: @B + insert-annotation Class.bound 0, ParameterizedType.typeArgument 0: @E + insert-annotation Class.bound -1, ParameterizedType.typeArgument 1: @C + insert-annotation Class.bound 0, ParameterizedType.typeArgument 1: @F + insert-annotation Class.bound -1: @A + insert-annotation Class.bound 0: @D + +package : +class ExtImpl$C2: + insert-annotation Class.bound 0, ParameterizedType.typeArgument 0: @B + insert-annotation Class.bound 1, ParameterizedType.typeArgument 0: @E + insert-annotation Class.bound 0, ParameterizedType.typeArgument 1: @C + insert-annotation Class.bound 1, ParameterizedType.typeArgument 1: @F + insert-annotation Class.bound 0: @A + insert-annotation Class.bound 1: @D +
diff --git a/annotation-file-utilities/tests/converted/FieldGenericArray.jaif b/annotation-file-utilities/tests/converted/FieldGenericArray.jaif new file mode 100644 index 0000000..2e6d510 --- /dev/null +++ b/annotation-file-utilities/tests/converted/FieldGenericArray.jaif
@@ -0,0 +1,17 @@ +package : +annotation @GenericType: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @InsideArray: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @OuterMostType: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldGenericArray: + + field field: + insert-annotation Variable.type, ArrayType.type, ParameterizedType.typeArgument 0: @GenericType + insert-annotation Variable.type, ArrayType.type: @InsideArray + insert-annotation Variable.type: @OuterMostType +
diff --git a/annotation-file-utilities/tests/converted/FieldMultiple.jaif b/annotation-file-utilities/tests/converted/FieldMultiple.jaif new file mode 100644 index 0000000..48b3f89 --- /dev/null +++ b/annotation-file-utilities/tests/converted/FieldMultiple.jaif
@@ -0,0 +1,8 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldMultiple: + + field d: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/FieldSimple.jaif b/annotation-file-utilities/tests/converted/FieldSimple.jaif new file mode 100644 index 0000000..a4539f9 --- /dev/null +++ b/annotation-file-utilities/tests/converted/FieldSimple.jaif
@@ -0,0 +1,9 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldSimple: + + field field: + insert-annotation Variable.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/FieldSimpleArray.jaif b/annotation-file-utilities/tests/converted/FieldSimpleArray.jaif new file mode 100644 index 0000000..6bed71e --- /dev/null +++ b/annotation-file-utilities/tests/converted/FieldSimpleArray.jaif
@@ -0,0 +1,13 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldSimpleArray: + + field field: + insert-annotation Variable.type, ArrayType.type: @java.lang.UnderInitialization + insert-annotation Variable.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/FieldSimpleGeneric.jaif b/annotation-file-utilities/tests/converted/FieldSimpleGeneric.jaif new file mode 100644 index 0000000..6b1f877 --- /dev/null +++ b/annotation-file-utilities/tests/converted/FieldSimpleGeneric.jaif
@@ -0,0 +1,13 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class FieldSimpleGeneric: + + field field: + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.Tainted + insert-annotation Variable.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/GenericAnnoBound.jaif b/annotation-file-utilities/tests/converted/GenericAnnoBound.jaif new file mode 100644 index 0000000..b0d113f --- /dev/null +++ b/annotation-file-utilities/tests/converted/GenericAnnoBound.jaif
@@ -0,0 +1,10 @@ +package : +annotation @Bla: + +package : +class GenericAnnoBound: + + method <init>(LGenericAnnoBound;Ljava/lang/Object;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @Bla +
diff --git a/annotation-file-utilities/tests/converted/GenericArg.jaif b/annotation-file-utilities/tests/converted/GenericArg.jaif new file mode 100644 index 0000000..b607f78 --- /dev/null +++ b/annotation-file-utilities/tests/converted/GenericArg.jaif
@@ -0,0 +1,29 @@ +package : +annotation @X: + +package : +class GenericArg: + + method mp(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @X + + method mr()LX;: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @X + + method foo(Ljava/lang/Number;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @X + + method bar()LZ;: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @X + +package : +class GenericArg$Tricky: + + method argh(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @X +
diff --git a/annotation-file-utilities/tests/converted/GenericCell.jaif b/annotation-file-utilities/tests/converted/GenericCell.jaif new file mode 100644 index 0000000..40761c3 --- /dev/null +++ b/annotation-file-utilities/tests/converted/GenericCell.jaif
@@ -0,0 +1,27 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @PolyRaw: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @ThisUnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class GenericCell: + + field internalList: + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.ThisUnderInitialization + insert-annotation Variable.type: @java.lang.ThisUnderInitialization + + method <init>(Ljava/util/List;)V: + return: + insert-annotation Method.parameter 0, Variable.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization + insert-annotation Method.parameter 0, Variable.type: @java.lang.UnderInitialization + + method getList()Ljava/util/List;: + return: + insert-annotation Method.type, ParameterizedType.typeArgument 0: @java.lang.PolyRaw + insert-annotation Method.parameter -1: @java.lang.PolyRaw + insert-annotation Method.type: @java.lang.PolyRaw +
diff --git a/annotation-file-utilities/tests/converted/GenericCellDoubled.jaif b/annotation-file-utilities/tests/converted/GenericCellDoubled.jaif new file mode 100644 index 0000000..c08daec --- /dev/null +++ b/annotation-file-utilities/tests/converted/GenericCellDoubled.jaif
@@ -0,0 +1,34 @@ +package : +annotation @X: + +package : +annotation @Y: + +package : +class GenericCellDoubled: + + field f: + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @X + + field g: + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @X + + field h: + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @X + + field i: + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @X + + field j1: + insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @X + + field j2: + insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @X + + field j3: + insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @X + insert-annotation Variable.type, ParameterizedType.typeArgument 1: @Y @X + + field k: + insert-annotation Variable.type: @Y +
diff --git a/annotation-file-utilities/tests/converted/GenericMultiLevel.jaif b/annotation-file-utilities/tests/converted/GenericMultiLevel.jaif new file mode 100644 index 0000000..1d6eebd --- /dev/null +++ b/annotation-file-utilities/tests/converted/GenericMultiLevel.jaif
@@ -0,0 +1,25 @@ +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @H: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @G: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class GenericMultiLevel: + + field field: + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.C + insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @java.lang.H + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.B + insert-annotation Variable.type, ParameterizedType.typeArgument 1: @java.lang.G + insert-annotation Variable.type: @java.lang.A +
diff --git a/annotation-file-utilities/tests/converted/ImplicitUpper.jaif b/annotation-file-utilities/tests/converted/ImplicitUpper.jaif new file mode 100644 index 0000000..f6edd90 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ImplicitUpper.jaif
@@ -0,0 +1,14 @@ +package : +annotation @A: + +package : +annotation @B: + +package : +class ExplicitUpper: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @A + +package : +class ImplicitUpper: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @B +
diff --git a/annotation-file-utilities/tests/converted/Initializers.jaif b/annotation-file-utilities/tests/converted/Initializers.jaif new file mode 100644 index 0000000..ca158c1 --- /dev/null +++ b/annotation-file-utilities/tests/converted/Initializers.jaif
@@ -0,0 +1,27 @@ +package : +annotation @X: + +package annotator.tests: +class Initializers: + + field s1: + insert-annotation Class.initializer 0, Block.statement 0, Variable.initializer, NewClass.identifier: @X + + field s2: + insert-annotation Class.initializer 1, Block.statement 0, Variable.initializer, NewClass.identifier: @X + + field o1: + insert-annotation Class.initializer 2, Block.statement 0, Variable.initializer, NewClass.identifier: @X + + field o2: + insert-annotation Class.initializer 3, Block.statement 0, Variable.initializer, NewClass.identifier: @X + +package annotator.tests: +class Initializers$MyEnum: + + field s: + insert-annotation Class.initializer 0, Block.statement 0, Variable.initializer, NewClass.identifier: @X + + field o: + insert-annotation Class.initializer 1, Block.statement 0, Variable.initializer, NewClass.identifier: @X +
diff --git a/annotation-file-utilities/tests/converted/InnerClass.jaif b/annotation-file-utilities/tests/converted/InnerClass.jaif new file mode 100644 index 0000000..e4c2856 --- /dev/null +++ b/annotation-file-utilities/tests/converted/InnerClass.jaif
@@ -0,0 +1,53 @@ +package : +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +annotation @D: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +class InnerClass: + + method m()V: + return: + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @A(value=5) + insert-annotation Method.body, Block.statement 5, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @D(value=5) + insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @A(value=3) + insert-annotation Method.body, Block.statement 5, If.condition, Parenthesized.expression, InstanceOf.type: @D(value=3) + insert-annotation Method.body, Block.statement 4, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @D(value=2) + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @A(value=4) + insert-annotation Method.body, Block.statement 5, If.thenStatement, Block.statement 0, Variable.type: @D(value=4) + insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @A(value=2) + insert-annotation Method.body, Block.statement 0, Variable.type: @A(value=1) + +package : +class InnerClass$1Inner: + + method m()V: + return: + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @B(value=5) + insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @B(value=3) + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @B(value=4) + insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @B(value=2) + insert-annotation Method.body, Block.statement 0, Variable.type: @B(value=1) + +package : +class InnerClass$1: + + method m()V: + return: + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @C(value=5) + insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @C(value=3) + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @C(value=4) + insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @C(value=2) + insert-annotation Method.body, Block.statement 0, Variable.type: @C(value=1) +
diff --git a/annotation-file-utilities/tests/converted/InnerClassAnonymous.jaif b/annotation-file-utilities/tests/converted/InnerClassAnonymous.jaif new file mode 100644 index 0000000..12eda14 --- /dev/null +++ b/annotation-file-utilities/tests/converted/InnerClassAnonymous.jaif
@@ -0,0 +1,27 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @NonNegative: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @InnerlyAnnotated: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InnerClassAnonymous$1: + + field serialVersionUID: + insert-annotation Variable.type: @java.lang.Tainted + +package annotator.tests: +class InnerClassAnonymous$2: + + field serialVersionUID: + insert-annotation Variable.type: @NonNegative + +package annotator.tests: +class InnerClassAnonymous$3: + + field serialVersionUID: + insert-annotation Variable.type: @InnerlyAnnotated +
diff --git a/annotation-file-utilities/tests/converted/InnerClassSimple.jaif b/annotation-file-utilities/tests/converted/InnerClassSimple.jaif new file mode 100644 index 0000000..cf14df0 --- /dev/null +++ b/annotation-file-utilities/tests/converted/InnerClassSimple.jaif
@@ -0,0 +1,12 @@ +package java.lang: +annotation @DoesNotExist: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InnerClassSimple$ActualInnerClass: @java.lang.DoesNotExist @java.lang.Tainted + + field d: + insert-annotation Variable.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/InnerReceivers.jaif b/annotation-file-utilities/tests/converted/InnerReceivers.jaif new file mode 100644 index 0000000..1e84d83 --- /dev/null +++ b/annotation-file-utilities/tests/converted/InnerReceivers.jaif
@@ -0,0 +1,170 @@ +package : +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @D: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @D1: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @E: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +annotation @F: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @F1: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @G: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +annotation @H: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @I: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @J: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @K: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @I1: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +annotation @L: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @M: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @N: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @O: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InnerReceivers$1$Inner: + + method m()V: + return: + insert-annotation Method.parameter -1: @A + + method m1()V: + return: + insert-annotation Method.parameter -1, Variable.type: @B + +package annotator.tests: +class InnerReceivers: + + method m()V: + return: + insert-annotation Method.parameter -1: @C + + method m1()V: + return: + insert-annotation Method.parameter -1, Variable.type: @D + + method m2()V: + return: + insert-annotation Method.parameter -1, Variable.type: @D1 + +package annotator.tests: +class InnerReceivers$Inner1: + + method m()V: + return: + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @E(value=2) + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @E(value=3) + insert-annotation Method.parameter -1, ParameterizedType.type, MemberSelect.expression: @E(value=1) + insert-annotation Method.parameter -1: @E(value=0) + + method m1()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @F + + method m2()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @F1 + +package annotator.tests: +class InnerReceivers$Inner1$Inner2: + + method m()V: + return: + insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.type, MemberSelect.expression: @G(value=4) + insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.typeArgument 0: @G(value=2) + insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.typeArgument 1: @G(value=3) + insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression: @G(value=1) + insert-annotation Method.parameter -1: @G(value=0) + + method m1()V: + return: + insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.type, MemberSelect.expression: @H + +package annotator.tests: +class InnerReceivers$StaticInner1: + + method m()V: + return: + insert-annotation Method.parameter -1: @I + + method m1()V: + return: + insert-annotation Method.parameter -1, Variable.type: @J + + method m2()V: + return: + insert-annotation Method.parameter -1, Variable.type: @K + +package annotator.tests: +class InnerReceivers$StaticInner3: + + method m()V: + return: + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @I1(value=1) + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @I1(value=2) + insert-annotation Method.parameter -1: @I1(value=0) + + method m1()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type: @J + + method m2()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type: @K + +package annotator.tests: +class Outer$StaticInner2: + + method m()V: + return: + insert-annotation Method.parameter -1: @L + + method m1()V: + return: + insert-annotation Method.parameter -1, Variable.type: @M + + method m2()V: + return: + insert-annotation Method.parameter -1, Variable.type: @N + +package annotator.tests: +class Outer$StaticInner2$StaticInner3: + + method m()V: + return: + insert-annotation Method.parameter -1: @O +
diff --git a/annotation-file-utilities/tests/converted/InnerTypeResolution.jaif b/annotation-file-utilities/tests/converted/InnerTypeResolution.jaif new file mode 100644 index 0000000..8359ecd --- /dev/null +++ b/annotation-file-utilities/tests/converted/InnerTypeResolution.jaif
@@ -0,0 +1,13 @@ +package java.lang: +annotation @Deprecated: + +package java.lang: +annotation @Tainted: + +package annotator.tests: +class InnerTypeResolution: + + method method01(Ljava/util/Map;)Ljava/util/Map$Entry;: @java.lang.Deprecated + return: + insert-annotation Method.parameter 0, Variable.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/InstanceOfMultiple.jaif b/annotation-file-utilities/tests/converted/InstanceOfMultiple.jaif new file mode 100644 index 0000000..1a737ca --- /dev/null +++ b/annotation-file-utilities/tests/converted/InstanceOfMultiple.jaif
@@ -0,0 +1,16 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InstanceOfMultiple: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, If.thenStatement, Block.statement 0, If.thenStatement, Block.statement 0, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.UnderInitialization + insert-annotation Method.body, Block.statement 0, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.Tainted + insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.UnderInitialization + insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type, ParameterizedType.typeArgument 0: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/InstanceOfSimple.jaif b/annotation-file-utilities/tests/converted/InstanceOfSimple.jaif new file mode 100644 index 0000000..921560b --- /dev/null +++ b/annotation-file-utilities/tests/converted/InstanceOfSimple.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class InstanceOfSimple: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/IntCell.jaif b/annotation-file-utilities/tests/converted/IntCell.jaif new file mode 100644 index 0000000..f9c17f3 --- /dev/null +++ b/annotation-file-utilities/tests/converted/IntCell.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class IntCell: + + method set(I)V: + return: + insert-annotation Method.parameter -1: @java.lang.UnderInitialization + + method get()I: + return: + insert-annotation Method.parameter -1: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/LocalArray.jaif b/annotation-file-utilities/tests/converted/LocalArray.jaif new file mode 100644 index 0000000..e333462 --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalArray.jaif
@@ -0,0 +1,14 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalArray: + + method foo()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type, ArrayType.type: @java.lang.UnderInitialization + insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/LocalClass.jaif b/annotation-file-utilities/tests/converted/LocalClass.jaif new file mode 100644 index 0000000..313cf84 --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalClass.jaif
@@ -0,0 +1,88 @@ +package : +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +class LocalClass: + + field f: + insert-annotation Variable.type: @A(value=1) + +package : +class LocalClass$1InnerLocalClass: + + field f: + insert-annotation Variable.type: @A(value=2) + +package : +class LocalClass$1: + + field f: + insert-annotation Variable.type: @A(value=3) + +package : +class LocalClass$1$Test: + + field f: + insert-annotation Variable.type: @A(value=4) + +package : +class LocalClass$1$Test$1: + + field f: + insert-annotation Variable.type: @A(value=41) + +package : +class LocalClass$1$Test$2: + + field f: + insert-annotation Variable.type: @A(value=42) + +package : +class LocalClass$2: + + field f: + insert-annotation Variable.type: @A(value=31) + +package : +class LocalClass$2InnerLocalClass: + + field f: + insert-annotation Variable.type: @A(value=5) + +package : +class LocalClass$2InnerLocalClass$Inner: + + field f: + insert-annotation Variable.type: @A(value=6) + +package : +class LocalClass$2InnerLocalClass$Inner$1: + + field f: + insert-annotation Variable.type: @A(value=7) + +package : +class LocalClass$2InnerLocalClass$Inner$2: + + field f: + insert-annotation Variable.type: @A(value=71) + +package : +class LocalClass$2InnerLocalClass$1OuterLocalClass: + + field f: + insert-annotation Variable.type: @A(value=10) + +package : +class LocalClass$1OuterLocalClass: + + field f: + insert-annotation Variable.type: @A(value=8) + +package : +class LocalClass$1OuterLocalClass$1InnerLocalClass: + + field f: + insert-annotation Variable.type: @A(value=9) +
diff --git a/annotation-file-utilities/tests/converted/LocalGeneric.jaif b/annotation-file-utilities/tests/converted/LocalGeneric.jaif new file mode 100644 index 0000000..aa53ec4 --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalGeneric.jaif
@@ -0,0 +1,14 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalGeneric: + + method foo()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type, ParameterizedType.typeArgument 0: @java.lang.Tainted + insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/LocalGenericShadow.jaif b/annotation-file-utilities/tests/converted/LocalGenericShadow.jaif new file mode 100644 index 0000000..4ecd93d --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalGenericShadow.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalGenericShadow: + + method method()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/LocalMultiple.jaif b/annotation-file-utilities/tests/converted/LocalMultiple.jaif new file mode 100644 index 0000000..2f16436 --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalMultiple.jaif
@@ -0,0 +1,14 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalMultiple: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.Tainted + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/LocalMultipleManyMethods.jaif b/annotation-file-utilities/tests/converted/LocalMultipleManyMethods.jaif new file mode 100644 index 0000000..46fb454 --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalMultipleManyMethods.jaif
@@ -0,0 +1,25 @@ +package java.lang: +annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalMultipleManyMethods: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.B + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.C + + method foo([Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.D + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.A +
diff --git a/annotation-file-utilities/tests/converted/LocalMultipleManyMethodsShifted.jaif b/annotation-file-utilities/tests/converted/LocalMultipleManyMethodsShifted.jaif new file mode 100644 index 0000000..3d1ce21 --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalMultipleManyMethodsShifted.jaif
@@ -0,0 +1,21 @@ +package java.lang: +annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalMultipleManyMethodsShifted: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 3, If.thenStatement, Block.statement 0, Variable.type: @java.lang.C + + method foo([Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.A + insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.D +
diff --git a/annotation-file-utilities/tests/converted/LocalSimple.jaif b/annotation-file-utilities/tests/converted/LocalSimple.jaif new file mode 100644 index 0000000..a329cf0 --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalSimple.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalSimple: + + method foo()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/LocalSimpleMultiple.jaif b/annotation-file-utilities/tests/converted/LocalSimpleMultiple.jaif new file mode 100644 index 0000000..391bc2f --- /dev/null +++ b/annotation-file-utilities/tests/converted/LocalSimpleMultiple.jaif
@@ -0,0 +1,18 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalSimpleMultiple: + + method foo()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.UnderInitialization + insert-annotation Method.body, Block.statement 2, Variable.type: @java.lang.Tainted + + method bar(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/MatchReturnValue.jaif b/annotation-file-utilities/tests/converted/MatchReturnValue.jaif new file mode 100644 index 0000000..39faa3a --- /dev/null +++ b/annotation-file-utilities/tests/converted/MatchReturnValue.jaif
@@ -0,0 +1,9 @@ +package java.lang: +annotation @A: + +package annotator.tests: +class MatchReturnValue: + + method clone()Lannotator/tests/MatchReturnValue;: @java.lang.A + return: +
diff --git a/annotation-file-utilities/tests/converted/MemberSelectTypes.jaif b/annotation-file-utilities/tests/converted/MemberSelectTypes.jaif new file mode 100644 index 0000000..55e9972 --- /dev/null +++ b/annotation-file-utilities/tests/converted/MemberSelectTypes.jaif
@@ -0,0 +1,136 @@ +package : +annotation @Anno: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package : +annotation @S: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @P: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @Q: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @R: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @O: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @N: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @M: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @L: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @C: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @F: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @D: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @E: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @I: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @G: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @H: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @K: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @J: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +annotation @B: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +class MemberSelectTypes: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @Anno(value=0) + insert-annotation Class.typeParameter 0, TypeParameter.bound 1: @Anno(value=1) + insert-annotation Class.bound -1: @Anno(value=2) + insert-annotation Class.bound 0: @Anno(value=3) + + field o: + insert-annotation Variable.type: @C + + field m1: + insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.type: @F + insert-annotation Variable.type, ParameterizedType.type: @D + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @E + + field m2: + insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.type: @I + insert-annotation Variable.type, ParameterizedType.type: @G + insert-annotation Variable.type, ParameterizedType.typeArgument 0: @H + + field m3: + insert-annotation Variable.type, ParameterizedType.typeArgument 0, MemberSelect.expression: @H + insert-annotation Variable.type, ParameterizedType.typeArgument 1: @I + + field m4: + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ParameterizedType.type: @K + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ParameterizedType.type: @I + insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type: @H + insert-annotation Variable.type, ParameterizedType.type: @G + + field i: + insert-annotation Variable.type, MemberSelect.expression: @J + + field s: + insert-annotation Variable.type: @K + + method m(Ljava/lang/Object;LMemberSelectTypes$Inner;LMemberSelectTypes$StaticInner;)Ljava/lang/Object;: + return: + insert-annotation Method.body, Block.statement 10, ExpressionStatement.expression, Assignment.expression, InstanceOf.type, MemberSelect.expression: @S + insert-annotation Method.body, Block.statement 4, ExpressionStatement.expression, Assignment.expression, NewClass.identifier, MemberSelect.expression: @P + insert-annotation Method.body, Block.statement 7, Variable.type, ArrayType.type, MemberSelect.expression: @Q + insert-annotation Method.body, Block.statement 7, Variable.initializer, NewArray.type 1: @R + insert-annotation Method.body, Block.statement 1, Variable.initializer, TypeCast.type, MemberSelect.expression: @O + insert-annotation Method.body, Block.statement 11, ExpressionStatement.expression, Assignment.expression, InstanceOf.type: @S + insert-annotation Method.body, Block.statement 3, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @P + insert-annotation Method.body, Block.statement 5, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @P + insert-annotation Method.body, Block.statement 6, Variable.type, ArrayType.type: @Q + insert-annotation Method.body, Block.statement 8, Variable.type, ArrayType.type: @Q + insert-annotation Method.body, Block.statement 1, Variable.type, MemberSelect.expression: @N + insert-annotation Method.parameter 1, Variable.type, MemberSelect.expression: @M + insert-annotation Method.body, Block.statement 9, Variable.initializer, InstanceOf.type: @S + insert-annotation Method.body, Block.statement 6, Variable.initializer, NewArray.type 1: @R + insert-annotation Method.body, Block.statement 8, Variable.initializer, NewArray.type 1: @R + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @O + insert-annotation Method.body, Block.statement 2, Variable.initializer, TypeCast.type: @O + insert-annotation Method.body, Block.statement 0, Variable.type: @N + insert-annotation Method.body, Block.statement 2, Variable.type: @N + insert-annotation Method.parameter 0, Variable.type: @M + insert-annotation Method.parameter 2, Variable.type: @M + insert-annotation Method.type: @L + +package : +class MemberSelectTypes$Inner: + + method m()V: + return: + insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression: @A + +package : +class MemberSelectTypes$StaticInner: + + method m()V: + return: + insert-annotation Method.parameter -1, Variable.type: @B +
diff --git a/annotation-file-utilities/tests/converted/MethodCompoundType.jaif b/annotation-file-utilities/tests/converted/MethodCompoundType.jaif new file mode 100644 index 0000000..4de003b --- /dev/null +++ b/annotation-file-utilities/tests/converted/MethodCompoundType.jaif
@@ -0,0 +1,10 @@ +package : +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + +package : +class MethodCompoundType: + + method m()[Ljava/util/Map;: + return: + insert-annotation Method.type: @A +
diff --git a/annotation-file-utilities/tests/converted/MethodMultiple.jaif b/annotation-file-utilities/tests/converted/MethodMultiple.jaif new file mode 100644 index 0000000..a12644d --- /dev/null +++ b/annotation-file-utilities/tests/converted/MethodMultiple.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class MethodMultiple: + + method foo(Ljava/lang/String;)Ljava/lang/String;: + return: + insert-annotation Method.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/NewArray.jaif b/annotation-file-utilities/tests/converted/NewArray.jaif new file mode 100644 index 0000000..962975d --- /dev/null +++ b/annotation-file-utilities/tests/converted/NewArray.jaif
@@ -0,0 +1,85 @@ +package : +annotation @X: + +package : +annotation @B: + +package : +annotation @A: + +package : +annotation @Z: + +package : +annotation @C: + +package : +annotation @D: + +package : +annotation @E: + +package : +class NewArray: + + field names01: + insert-annotation Variable.initializer, NewArray.type 1: @X + + field names02: + insert-annotation Variable.initializer, NewArray.type 1: @X + + field names03: + insert-annotation Variable.initializer, NewArray.type 1: @X + + field table1: + insert-annotation Variable.initializer, NewArray.type 0: @B + insert-annotation Variable.initializer, NewArray.type 1: @A + + field table2: + insert-annotation Variable.initializer, NewArray.type 0: @B + insert-annotation Variable.initializer, NewArray.type 1: @A + + field names0: + insert-annotation Variable.initializer, NewArray.type 0: @Z + + field names1: + insert-annotation Variable.initializer, NewArray.type 1: @A + + field names2: + insert-annotation Variable.initializer, NewArray.type 2: @B + + field names3: + insert-annotation Variable.initializer, NewArray.type 3: @C + + field names4: + insert-annotation Variable.initializer, NewArray.type 4: @D + + field names5: + insert-annotation Variable.initializer, NewArray.type 5: @E + + field names10: + insert-annotation Variable.initializer, NewArray.type 0: @Z + + field names11: + insert-annotation Variable.initializer, NewArray.type 1: @A + + field names12: + insert-annotation Variable.initializer, NewArray.type 2: @B + + field names13: + insert-annotation Variable.initializer, NewArray.type 3: @C + + field names14: + insert-annotation Variable.initializer, NewArray.type 4: @D + + field names15: + insert-annotation Variable.initializer, NewArray.type 5: @E + + field lists: + insert-annotation Variable.type: @B + insert-annotation Variable.type, ArrayType.type: @Z + insert-annotation Variable.type, ArrayType.type, ParameterizedType.typeArgument 0: @A + insert-annotation Variable.initializer, NewArray.type 0: @E + insert-annotation Variable.initializer, NewArray.type 1: @C + insert-annotation Variable.initializer, NewArray.type 1, ParameterizedType.typeArgument 0: @D +
diff --git a/annotation-file-utilities/tests/converted/NewGeneric.jaif b/annotation-file-utilities/tests/converted/NewGeneric.jaif new file mode 100644 index 0000000..211ed3a --- /dev/null +++ b/annotation-file-utilities/tests/converted/NewGeneric.jaif
@@ -0,0 +1,15 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class NewGeneric: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.Tainted + insert-annotation Method.body, Block.statement 1, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/NewInAnnotatedVariable.jaif b/annotation-file-utilities/tests/converted/NewInAnnotatedVariable.jaif new file mode 100644 index 0000000..9eacec7 --- /dev/null +++ b/annotation-file-utilities/tests/converted/NewInAnnotatedVariable.jaif
@@ -0,0 +1,18 @@ +package : +annotation @NonNull: + +package : +class NewInAnnotatedVariable: + + field b1: + insert-annotation Variable.initializer, NewClass.identifier: @NonNull + + field b2: + insert-annotation Variable.initializer, NewClass.identifier, AnnotatedType.underlyingType: @NonNull + + field b4: + insert-annotation Variable.type, ArrayType.type: @NonNull + insert-annotation Variable.initializer, NewArray.type 0: @NonNull + insert-annotation Variable.initializer, NewArray.type 1: @NonNull + insert-annotation Variable.type: @NonNull +
diff --git a/annotation-file-utilities/tests/converted/NewMultiple.jaif b/annotation-file-utilities/tests/converted/NewMultiple.jaif new file mode 100644 index 0000000..7109c41 --- /dev/null +++ b/annotation-file-utilities/tests/converted/NewMultiple.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class NewMultiple: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.Tainted + + method bar(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/NewPackage.jaif b/annotation-file-utilities/tests/converted/NewPackage.jaif new file mode 100644 index 0000000..fd0697a --- /dev/null +++ b/annotation-file-utilities/tests/converted/NewPackage.jaif
@@ -0,0 +1,10 @@ +package : +annotation @X: + +package : +class NewPackage: + + method <init>()V: + return: + insert-annotation Method.body, Block.statement 1, Variable.initializer, NewClass.identifier: @X +
diff --git a/annotation-file-utilities/tests/converted/NewSimple.jaif b/annotation-file-utilities/tests/converted/NewSimple.jaif new file mode 100644 index 0000000..ee97c85 --- /dev/null +++ b/annotation-file-utilities/tests/converted/NewSimple.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class NewSimple: + + method foo()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/NonClass.jaif b/annotation-file-utilities/tests/converted/NonClass.jaif new file mode 100644 index 0000000..fc2d2d4 --- /dev/null +++ b/annotation-file-utilities/tests/converted/NonClass.jaif
@@ -0,0 +1,53 @@ +package : +annotation @X: + +package : +annotation @V: + int value + +package : +class NonClass$A: + + method m(Ljava/lang/Object;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @X + +package : +class NonClass$B: + + method m()V: + return: + insert-annotation Method.body, Block.statement 0, Variable.type: @X + +package : +class NonClass$C: + + method value()Ljava/lang/String;: + return: + insert-annotation Method.type: @X + +package : +class NonClass$E: + + field D: + insert-annotation Variable.initializer, NewClass.argument 0, TypeCast.type: @V(value=1) + insert-annotation Variable.initializer, NewClass.identifier: @V(value=2) + insert-annotation Variable.type: @V(value=3) + +package : +class NonClass$E: + + field a: + insert-annotation Variable.type: @X + +package : +class NonClass$E: + + method <init>(Ljava/lang/String;)V: + insert-annotation Method.body, Block.statement 1, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @V(value=0) + +package : +class NonClass$I: + insert-annotation Class.bound 0: @X + insert-annotation Class.bound 0, ParameterizedType.typeArgument 0: @X +
diff --git a/annotation-file-utilities/tests/converted/NonStandardSpacing.jaif b/annotation-file-utilities/tests/converted/NonStandardSpacing.jaif new file mode 100644 index 0000000..805bf7f --- /dev/null +++ b/annotation-file-utilities/tests/converted/NonStandardSpacing.jaif
@@ -0,0 +1,34 @@ +package : +annotation @Anno: + +package : +class NonStandardSpacing: + + method m1()V: + return: + insert-typecast Block.statement 0, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 1, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 2, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 3, Variable.initializer, Binary.leftOperand: @Anno int + insert-typecast Block.statement 4, Variable.initializer, Binary.leftOperand: @Anno int + + method m2()V: + return: + insert-annotation Method.parameter -1: @Anno + + method m3()V: + return: + insert-annotation Method.parameter -1: @Anno + + method m4()V: + return: + insert-annotation Method.parameter -1: @Anno + + method m5()V: + return: + insert-annotation Method.parameter -1: @Anno + + method m7()V: + return: + insert-annotation Method.parameter -1: @Anno +
diff --git a/annotation-file-utilities/tests/converted/Package.jaif b/annotation-file-utilities/tests/converted/Package.jaif new file mode 100644 index 0000000..8691233 --- /dev/null +++ b/annotation-file-utilities/tests/converted/Package.jaif
@@ -0,0 +1,9 @@ +package : +annotation @A: @java.lang.annotation.Target(value={TYPE_USE}) + +package pkg.name.here: +class Package: + + field o: + insert-annotation Variable.type: @A +
diff --git a/annotation-file-utilities/tests/converted/ParseType.jaif b/annotation-file-utilities/tests/converted/ParseType.jaif new file mode 100644 index 0000000..00d37cf --- /dev/null +++ b/annotation-file-utilities/tests/converted/ParseType.jaif
@@ -0,0 +1,21 @@ +package : +annotation @A: + +package : +class ParseType: + + method m(Ljava/lang/Object;)V: + return: + insert-typecast Block.statement 0, Variable.initializer: @A String + insert-typecast Block.statement 1, Variable.initializer: @A Map<String, Integer> + insert-typecast Block.statement 2, Variable.initializer: @A String[] + insert-typecast Block.statement 3, Variable.initializer: @A String[][] + insert-typecast Block.statement 4, Variable.initializer: @A Map<String[], Integer> + insert-typecast Block.statement 5, Variable.initializer: @A Map<String[][], Integer> + insert-typecast Block.statement 6, Variable.initializer: @A Map<?, ?> + insert-typecast Block.statement 7, Variable.initializer: @A Map<? extends String, ? super List<Integer>> + insert-typecast Block.statement 8, Variable.initializer: @A Map<List<String>, List<String>[]> + insert-typecast Block.statement 9, Variable.initializer: @A Map.Entry<String, Integer> + insert-typecast Block.statement 10, Variable.initializer: @A Map.Entry<String, Integer>[] + insert-typecast Block.statement 11, Variable.initializer: @A ParseType<String>.Inner<Integer>[] +
diff --git a/annotation-file-utilities/tests/converted/ReceiverWithThrows.jaif b/annotation-file-utilities/tests/converted/ReceiverWithThrows.jaif new file mode 100644 index 0000000..b56c968 --- /dev/null +++ b/annotation-file-utilities/tests/converted/ReceiverWithThrows.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class ReceiverWithThrows: + + method foo()V: + return: + insert-annotation Method.parameter -1: @java.lang.UnderInitialization + + method bar()V: + return: + insert-annotation Method.parameter -1: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/Receivers.jaif b/annotation-file-utilities/tests/converted/Receivers.jaif new file mode 100644 index 0000000..754d6cb --- /dev/null +++ b/annotation-file-utilities/tests/converted/Receivers.jaif
@@ -0,0 +1,136 @@ +package checkers.tainting.quals: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotation: +annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE}) + int value + +package annotation: +annotation @A: + +package annotation: +annotation @B: + +package annotation: +annotation @C: + +package annotator.tests: +class Receivers: + + method m()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method spaces()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method m(I)V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method spaces(I)V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method m(Ljava/lang/String;)V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers2: + + method m()V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + + method spaces()V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + + method m(I)V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + + method spaces(I)V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers3: + + method m()V: + return: + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + + method m(I)V: + return: + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers4: + + method m()V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + + method m(I)V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers5: + + method m()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers6: + + method m()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers7: + + method m()V: + return: + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0) + insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1) + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers8: + + method m()V: + return: + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + +package annotator.tests: +class Receivers9: + + method m()V: + return: + insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C + +package annotator.tests: +class Receivers10: + + method m()V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.A + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.B + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted + + method m(Lannotator/tests/Receivers10;)V: + return: + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.A + insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.B + insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted +
diff --git a/annotation-file-utilities/tests/converted/TypeCastGeneric.jaif b/annotation-file-utilities/tests/converted/TypeCastGeneric.jaif new file mode 100644 index 0000000..14468ea --- /dev/null +++ b/annotation-file-utilities/tests/converted/TypeCastGeneric.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeCastGeneric: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/TypeCastMultiple.jaif b/annotation-file-utilities/tests/converted/TypeCastMultiple.jaif new file mode 100644 index 0000000..fffaf2e --- /dev/null +++ b/annotation-file-utilities/tests/converted/TypeCastMultiple.jaif
@@ -0,0 +1,15 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeCastMultiple: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 2, Variable.initializer, TypeCast.expression, Parenthesized.expression, TypeCast.type: @java.lang.Tainted + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization + insert-annotation Method.body, Block.statement 1, Variable.initializer, TypeCast.type: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/TypeCastSimple.jaif b/annotation-file-utilities/tests/converted/TypeCastSimple.jaif new file mode 100644 index 0000000..d040dd6 --- /dev/null +++ b/annotation-file-utilities/tests/converted/TypeCastSimple.jaif
@@ -0,0 +1,10 @@ +package java.lang: +annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeCastSimple: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/converted/TypeParamMethod.jaif b/annotation-file-utilities/tests/converted/TypeParamMethod.jaif new file mode 100644 index 0000000..655d6e3 --- /dev/null +++ b/annotation-file-utilities/tests/converted/TypeParamMethod.jaif
@@ -0,0 +1,22 @@ +package java.lang: +annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeParamMethod: + + method foo(Ljava/lang/Object;)V: + return: + insert-annotation Method.typeParameter 0: @java.lang.Tainted + + method foo2(Ljava/util/Date;)V: + return: + insert-annotation Method.typeParameter 0: @java.lang.Tainted + + method foo(Ljava/lang/Object;Ljava/lang/Object;)V: + return: + insert-annotation Method.typeParameter 1: @java.lang.Tainted + + method foo2(Ljava/util/Date;Ljava/util/Date;)V: + return: + insert-annotation Method.typeParameter 1: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/converted/Varargs.jaif b/annotation-file-utilities/tests/converted/Varargs.jaif new file mode 100644 index 0000000..a1a3cbc --- /dev/null +++ b/annotation-file-utilities/tests/converted/Varargs.jaif
@@ -0,0 +1,25 @@ +package java.lang: +annotation @Nullable: + +package annotator.tests: +class Varargs: + + method m1([Ljava/lang/String;)V: + return: + insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable + + method m2([Ljava/lang/String;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable + + method m3([Ljava/lang/String;)V: + return: + insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable + insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable + + method m4([Ljava/lang/Comparable;)V: + return: + insert-annotation Method.parameter 0, Variable.type, ArrayType.type, ParameterizedType.typeArgument 0: @java.lang.Nullable + insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable + insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable +
diff --git a/annotation-file-utilities/tests/converted/WildcardAnnoBound.jaif b/annotation-file-utilities/tests/converted/WildcardAnnoBound.jaif new file mode 100644 index 0000000..85be198 --- /dev/null +++ b/annotation-file-utilities/tests/converted/WildcardAnnoBound.jaif
@@ -0,0 +1,58 @@ +package : +annotation @Bla: + +package : +annotation @A: + +package : +annotation @D: + +package : +annotation @H: + +package : +annotation @E: + +package : +annotation @G: + +package : +annotation @I: + +package : +annotation @B: + +package : +annotation @C: + +package : +annotation @F: + +package : +annotation @J: + +package : +class WildcardAnnoBound: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0, ParameterizedType.typeArgument 0, Wildcard.bound: @Bla + + method <init>(LWildcardAnnoBound;Ljava/util/List;)V: + return: + insert-annotation Method.parameter 0, Variable.type: @Bla + +package : +class NoBound: + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @A + +package : +class Bounds: + insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0, ArrayType.type: @D + insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0, Wildcard.bound: @H + insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0: @E + insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0: @G + insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 1: @I + insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @B + insert-annotation Class.typeParameter 0, TypeParameter.bound 1: @C + insert-annotation Class.typeParameter 0, TypeParameter.bound 2: @F + insert-annotation Class.typeParameter 0: @A + insert-annotation Class.typeParameter 1: @J +
diff --git a/annotation-file-utilities/tests/converted/package-info.jaif b/annotation-file-utilities/tests/converted/package-info.jaif new file mode 100644 index 0000000..d8d107c --- /dev/null +++ b/annotation-file-utilities/tests/converted/package-info.jaif
@@ -0,0 +1,4 @@ +package java.lang: +annotation @OnThePackage: @java.lang.annotation.Retention(value=RUNTIME) + +package annotator.tests: @java.lang.OnThePackage
diff --git a/annotation-file-utilities/tests/package-info.goal b/annotation-file-utilities/tests/package-info.goal new file mode 100644 index 0000000..b016058 --- /dev/null +++ b/annotation-file-utilities/tests/package-info.goal
@@ -0,0 +1,2 @@ +@java.lang.OnThePackage +package annotator.tests;
diff --git a/annotation-file-utilities/tests/package-info.jaif b/annotation-file-utilities/tests/package-info.jaif new file mode 100644 index 0000000..e649e6c --- /dev/null +++ b/annotation-file-utilities/tests/package-info.jaif
@@ -0,0 +1,4 @@ +package java.lang: +annotation @OnThePackage: @Retention(value=RUNTIME) + +package annotator.tests: @java.lang.OnThePackage
diff --git a/annotation-file-utilities/tests/package-info.java b/annotation-file-utilities/tests/package-info.java new file mode 100644 index 0000000..4efcdd1 --- /dev/null +++ b/annotation-file-utilities/tests/package-info.java
@@ -0,0 +1 @@ +package annotator.tests;
diff --git a/annotation-file-utilities/tests/source-extension/ClassTV3.goal b/annotation-file-utilities/tests/source-extension/ClassTV3.goal new file mode 100644 index 0000000..837414a --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/ClassTV3.goal
@@ -0,0 +1,5 @@ +public class ClassTV3<X extends @GUT.quals.Peer Object, Y extends @GUT.quals.Rep Object, Z extends @GUT.quals.Any Object> { + X f; + Y g; + Z h; +}
diff --git a/annotation-file-utilities/tests/source-extension/ClassTV3.jaif b/annotation-file-utilities/tests/source-extension/ClassTV3.jaif new file mode 100644 index 0000000..01ad5eb --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/ClassTV3.jaif
@@ -0,0 +1,16 @@ +package GUT.quals: +annotation @Peer: +annotation @Rep: +annotation @Any: + +package: +class ClassTV3: +bound 0 & 0: @GUT.quals.Peer + +package: +class ClassTV3: +bound 1 & 0: @GUT.quals.Rep + +package: +class ClassTV3: +bound 2 & 0: @GUT.quals.Any \ No newline at end of file
diff --git a/annotation-file-utilities/tests/source-extension/ClassTV3.java b/annotation-file-utilities/tests/source-extension/ClassTV3.java new file mode 100644 index 0000000..0e25137 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/ClassTV3.java
@@ -0,0 +1,5 @@ +public class ClassTV3<X, Y, Z> { + X f; + Y g; + Z h; +}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNew.goal b/annotation-file-utilities/tests/source-extension/FieldNew.goal new file mode 100644 index 0000000..79ff204 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/FieldNew.goal
@@ -0,0 +1,6 @@ +package annotator.tests; + +public class FieldNew { + @GUT.quals.Peer + Object f = new @GUT.quals.Rep FieldNew(); +}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNew.jaif b/annotation-file-utilities/tests/source-extension/FieldNew.jaif new file mode 100644 index 0000000..61f676c --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/FieldNew.jaif
@@ -0,0 +1,14 @@ +package GUT.quals: +annotation @Peer: +annotation @Rep: + +package annotator.tests: +class FieldNew: +field f: +type: @GUT.quals.Peer + +package annotator.tests: +class FieldNew: +field f: +new *0: @GUT.quals.Rep +
diff --git a/annotation-file-utilities/tests/source-extension/FieldNew.java b/annotation-file-utilities/tests/source-extension/FieldNew.java new file mode 100644 index 0000000..3859777 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/FieldNew.java
@@ -0,0 +1,5 @@ +package annotator.tests; + +public class FieldNew { + Object f = new FieldNew(); +}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNewComplex.goal b/annotation-file-utilities/tests/source-extension/FieldNewComplex.goal new file mode 100644 index 0000000..e1fe8f4 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/FieldNewComplex.goal
@@ -0,0 +1,11 @@ +package annotator.tests; + +public class FieldNewComplex { + @GUT.quals.Rep + FieldNewComplex m(@GUT.quals.Peer FieldNewComplex a, @GUT.quals.Peer FieldNewComplex b, @GUT.quals.Rep FieldNewComplex c) { + return null; + } + + @GUT.quals.Rep + FieldNewComplex f = m(new @GUT.quals.Peer FieldNewComplex(), new @GUT.quals.Peer FieldNewComplex(), new @GUT.quals.Rep FieldNewComplex()); +}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNewComplex.jaif b/annotation-file-utilities/tests/source-extension/FieldNewComplex.jaif new file mode 100644 index 0000000..1c0694f --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/FieldNewComplex.jaif
@@ -0,0 +1,47 @@ +package GUT.quals: +annotation @Rep: +annotation @Peer: +annotation @Any: + +package annotator.tests: +class FieldNewComplex: +field f: +new *1: @GUT.quals.Peer + +package annotator.tests: +class FieldNewComplex: +field f: +new *2: @GUT.quals.Rep + +package annotator.tests: +class FieldNewComplex: +method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;: +parameter 1: +type: @GUT.quals.Peer + +package annotator.tests: +class FieldNewComplex: +field f: +new *0: @GUT.quals.Peer + +package annotator.tests: +class FieldNewComplex: +field f: +type: @GUT.quals.Rep + +package annotator.tests: +class FieldNewComplex: +method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;: +return: @GUT.quals.Rep + +package annotator.tests: +class FieldNewComplex: +method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;: +parameter 2: +type: @GUT.quals.Rep + +package annotator.tests: +class FieldNewComplex: +method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;: +parameter 0: +type: @GUT.quals.Peer
diff --git a/annotation-file-utilities/tests/source-extension/FieldNewComplex.java b/annotation-file-utilities/tests/source-extension/FieldNewComplex.java new file mode 100644 index 0000000..3c1bb3a --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/FieldNewComplex.java
@@ -0,0 +1,9 @@ +package annotator.tests; + +public class FieldNewComplex { + FieldNewComplex m(FieldNewComplex a, FieldNewComplex b, FieldNewComplex c) { + return null; + } + + FieldNewComplex f = m(new FieldNewComplex(), new FieldNewComplex(), new FieldNewComplex()); +}
diff --git a/annotation-file-utilities/tests/source-extension/LocalVariables.goal b/annotation-file-utilities/tests/source-extension/LocalVariables.goal new file mode 100644 index 0000000..ef7dca6 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/LocalVariables.goal
@@ -0,0 +1,9 @@ +package annotator.tests; + +public class LocalVariables { + public void foo() { + /*Mut*/ @java.lang.UnderInitialization Object a = null; + Object b = null; + Object c = null; + } +}
diff --git a/annotation-file-utilities/tests/source-extension/LocalVariables.jaif b/annotation-file-utilities/tests/source-extension/LocalVariables.jaif new file mode 100644 index 0000000..7a5e000 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/LocalVariables.jaif
@@ -0,0 +1,11 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class LocalVariables: + + method <init>()V: + + method foo()V: + local a: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/source-extension/LocalVariables.java b/annotation-file-utilities/tests/source-extension/LocalVariables.java new file mode 100644 index 0000000..a742952 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/LocalVariables.java
@@ -0,0 +1,9 @@ +package annotator.tests; + +public class LocalVariables { + public void foo() { + /*Mut*/ Object a = null; + Object b = null; + Object c = null; + } +}
diff --git a/annotation-file-utilities/tests/source-extension/Makefile b/annotation-file-utilities/tests/source-extension/Makefile new file mode 100644 index 0000000..27693af --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/Makefile
@@ -0,0 +1,87 @@ +# Very rough testing framework for the annotator. Running 'make all' will +# look for all myClass.goal files in this directory, run the annotator on the +# corresponding .jaif and .java files, and then output the difference in a +# myClass.diff file in this directory. +# +# To test just one file, use (for example) 'make myClass.diff'. + +# Put user-specific changes in your own Makefile.user. +# Make will silently continue if that file does not exist. +-include ../Makefile.user + +# Override these in Makefile.user if the java and javac commands are not on +# your execution path. Example from Makefile.user: +# JAVA=${JAVA_HOME}/bin/java +# JAVAC=${JAVA_HOME}/bin/javac +JAVA?=java +JAVAC?=javac + +export SHELL=/bin/bash -o pipefail + + +DIFFS := $(wildcard *.goal) +DISABLED := $(shell grep -le "@skip-test" $(DIFFS)) +FILTERED := $(filter-out $(DISABLED),$(DIFFS)) +DIFFS := $(patsubst %.goal, %.diff, $(FILTERED)) +SRC := $(patsubst %.goal, %.java, $(FILTERED)) +OUT := $(patsubst %.goal, %.output, $(FILTERED)) + +DEBUG := +# Use this to enable some debugging. +# DEBUG := --debug + +default : all + +.PHONY: all +all : $(DIFFS) results + +# Display results of all .diff files. +.PHONY: results +results: ../bin/VerifyDiffs.class + @echo "" + @echo "=== RESULTS ===" + @echo "" + @$(JAVA) -cp bin:../bin VerifyDiffs --show_all + +# Remakes the little java program that checks and compares diffs +../bin/VerifyDiffs.class : ../VerifyDiffs.java + @$(JAVAC) -g -cp ../../bincompile -d ../bin ../VerifyDiffs.java + +# Compiles all the test cases (be verbose about this). +.PHONY: compile +compile : $(SRC) + mkdir -p bin + $(JAVAC) -g -cp ../../bin -d bin $(SRC) + +# Compiles just one test case +.PRECIOUS : bin/annotator/tests/%.class +bin/annotator/tests/%.class: %.java + mkdir -p bin + $(JAVAC) -g -cp bin:../../bin -d bin -sourcepath . $< + +# Actually runs the annotator to create the annotated java file. +.PRECIOUS: %.output +%.output: %.jaif %.java bin/annotator/tests/%.class ../../lib/plume-core.jar ../../bin ../../annotation-file-utilities.jar + $(JAVA) \ + -cp ../../bin:../../annotation-file-utilities.jar:bin \ + annotator.Main \ + ${DEBUG} \ + --abbreviate=false \ + -d $*-output \ + $*.jaif \ + $*.java \ + 2>&1 | tee $*.log + find "$*-output" -name '*.java' -print | xargs cat > "$*.output" + rm -rf $*-output + +# Compare the output of the annotator and the goal file. +%.diff: %.goal %.output + -diff -u $*.goal $*.output >& $*.diff + +# Remove all .diff, .log files from the tests directory. +.PHONY: clean +clean : + rm -rf bin + rm -f *.diff + rm -f *.log + rm -f *.output
diff --git a/annotation-file-utilities/tests/source-extension/MethodNew.goal b/annotation-file-utilities/tests/source-extension/MethodNew.goal new file mode 100644 index 0000000..d9bf380 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/MethodNew.goal
@@ -0,0 +1,11 @@ +package annotator.tests; + +public class MethodNew { + void m() { + Object l = new @GUT.quals.Rep MethodNew(); + } + + void m(Object p) { + Object x = new @GUT.quals.Peer MethodNew(); + } +}
diff --git a/annotation-file-utilities/tests/source-extension/MethodNew.jaif b/annotation-file-utilities/tests/source-extension/MethodNew.jaif new file mode 100644 index 0000000..93a6a62 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/MethodNew.jaif
@@ -0,0 +1,15 @@ +package GUT.quals: +annotation @Peer: +annotation @Rep: + +package annotator.tests: +class MethodNew: +method m(Ljava/lang/Object;)V: +new *0: @GUT.quals.Peer + +package annotator.tests: +class MethodNew: +method m()V: +new *0: @GUT.quals.Rep + +
diff --git a/annotation-file-utilities/tests/source-extension/MethodNew.java b/annotation-file-utilities/tests/source-extension/MethodNew.java new file mode 100644 index 0000000..a41ad90 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/MethodNew.java
@@ -0,0 +1,11 @@ +package annotator.tests; + +public class MethodNew { + void m() { + Object l = new MethodNew(); + } + + void m(Object p) { + Object x = new MethodNew(); + } +}
diff --git a/annotation-file-utilities/tests/source-extension/SameName.goal b/annotation-file-utilities/tests/source-extension/SameName.goal new file mode 100644 index 0000000..670fb63 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/SameName.goal
@@ -0,0 +1,11 @@ +package annotator.tests; + +public class SameName { + void m() { + if (5==6) { + Object a = null; + } else { + @java.lang.UnderInitialization Object a = null; + } + } +}
diff --git a/annotation-file-utilities/tests/source-extension/SameName.jaif b/annotation-file-utilities/tests/source-extension/SameName.jaif new file mode 100644 index 0000000..f362460 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/SameName.jaif
@@ -0,0 +1,12 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class SameName: + + method <init>()V: + + method m()V: + local a *1: + type: @java.lang.UnderInitialization +
diff --git a/annotation-file-utilities/tests/source-extension/SameName.java b/annotation-file-utilities/tests/source-extension/SameName.java new file mode 100644 index 0000000..0e1d885 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/SameName.java
@@ -0,0 +1,11 @@ +package annotator.tests; + +public class SameName { + void m() { + if (5==6) { + Object a = null; + } else { + Object a = null; + } + } +}
diff --git a/annotation-file-utilities/tests/source-extension/StaticInit.goal b/annotation-file-utilities/tests/source-extension/StaticInit.goal new file mode 100644 index 0000000..7ec6528 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/StaticInit.goal
@@ -0,0 +1,21 @@ +package annotator.tests; + +public class StaticInit { + static void blabla() {} + + static { + @GUT.quals.Peer + Object o = new @GUT.quals.Peer Integer(5); + if (o instanceof Integer) { + @GUT.quals.Peer + Object o2 = new @GUT.quals.Peer Object(); + } + } + + void m() { if (true) {} else {} } + + static { + @GUT.quals.Rep + StaticInit si = new @GUT.quals.Rep StaticInit(); + } +}
diff --git a/annotation-file-utilities/tests/source-extension/StaticInit.jaif b/annotation-file-utilities/tests/source-extension/StaticInit.jaif new file mode 100644 index 0000000..3e98587 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/StaticInit.jaif
@@ -0,0 +1,37 @@ +package GUT.quals: +annotation @Peer: +annotation @Rep: + + +package annotator.tests: +class StaticInit: +staticinit *0: +new *1: @GUT.quals.Peer + +package annotator.tests: +class StaticInit: +staticinit *1: +new *0: @GUT.quals.Rep + +package annotator.tests: +class StaticInit: +staticinit *0: +new *0: @GUT.quals.Peer + +package annotator.tests: +class StaticInit: +staticinit *0: +local o2: +type: @GUT.quals.Peer + +package annotator.tests: +class StaticInit: +staticinit *1: +local si: +type: @GUT.quals.Rep + +package annotator.tests: +class StaticInit: +staticinit *0: +local o: +type: @GUT.quals.Peer
diff --git a/annotation-file-utilities/tests/source-extension/StaticInit.java b/annotation-file-utilities/tests/source-extension/StaticInit.java new file mode 100644 index 0000000..fcf24da --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/StaticInit.java
@@ -0,0 +1,18 @@ +package annotator.tests; + +public class StaticInit { + static void blabla() {} + + static { + Object o = new Integer(5); + if (o instanceof Integer) { + Object o2 = new Object(); + } + } + + void m() { if (true) {} else {} } + + static { + StaticInit si = new StaticInit(); + } +}
diff --git a/annotation-file-utilities/tests/source-extension/TypeCastMultiple.goal b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.goal new file mode 100644 index 0000000..aabb5dd --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.goal
@@ -0,0 +1,16 @@ +package annotator.tests; + +import java.util.List; +import java.util.LinkedList; + +public class TypeCastMultiple { + public void foo(Object o) { + List myList = (@java.lang.UnderInitialization List) o; + myList = new @java.lang.UnderInitialization LinkedList(); + if (myList instanceof @java.lang.UnderInitialization List) { + } + Integer i = (@java.lang.Tainted Integer) o; + System.out.println(myList); + System.out.println(i); + } +}
diff --git a/annotation-file-utilities/tests/source-extension/TypeCastMultiple.jaif b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.jaif new file mode 100644 index 0000000..25b743b --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.jaif
@@ -0,0 +1,17 @@ +package java.lang: +annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package java.lang: +annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE}) + +package annotator.tests: +class TypeCastMultiple: + + method <init>()V: + + method foo(Ljava/lang/Object;)V: + typecast *0: @java.lang.UnderInitialization + new *0: @java.lang.UnderInitialization + instanceof *0: @java.lang.UnderInitialization + typecast *1: @java.lang.Tainted +
diff --git a/annotation-file-utilities/tests/source-extension/TypeCastMultiple.java b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.java new file mode 100644 index 0000000..baf4874 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.java
@@ -0,0 +1,16 @@ +package annotator.tests; + +import java.util.List; +import java.util.LinkedList; + +public class TypeCastMultiple { + public void foo(Object o) { + List myList = (List) o; + myList = new LinkedList(); + if (myList instanceof List) { + } + Integer i = (Integer) o; + System.out.println(myList); + System.out.println(i); + } +}
diff --git a/annotation-file-utilities/tests/source-extension/Wildcards.goal b/annotation-file-utilities/tests/source-extension/Wildcards.goal new file mode 100644 index 0000000..69696f6 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/Wildcards.goal
@@ -0,0 +1,12 @@ +import java.util.List; +import java.util.Map; + +public class Wildcards { + @GUT.quals.Rep + List<? extends @GUT.quals.Rep Object> l1; + @GUT.quals.Rep + List<? extends @GUT.quals.Rep Object> l2; + Map<? extends @GUT.quals.Peer Object, ? extends @GUT.quals.Rep Object> l3; + Map<@GUT.quals.Rep ? extends @GUT.quals.Peer Map<@GUT.quals.Rep ? extends @GUT.quals.Peer Object, @GUT.quals.Rep String>, @GUT.quals.Peer Object> l4; +} +
diff --git a/annotation-file-utilities/tests/source-extension/Wildcards.jaif b/annotation-file-utilities/tests/source-extension/Wildcards.jaif new file mode 100644 index 0000000..ae318ea --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/Wildcards.jaif
@@ -0,0 +1,74 @@ +package GUT.quals: +annotation @Peer: +annotation @Rep: + +package: +class Wildcards: +field l1: +type: +inner-type 3, 0, 2, 0: @GUT.quals.Rep + +package: +class Wildcards: +field l1: +type: @GUT.quals.Rep + +package: +class Wildcards: +field l2: +type: @GUT.quals.Rep + +package: +class Wildcards: +field l2: +type: +inner-type 3, 0, 2, 0: @GUT.quals.Rep + +package: +class Wildcards: +field l3: +type: +inner-type 3, 0, 2, 0: @GUT.quals.Peer + +package: +class Wildcards: +field l3: +type: +inner-type 3, 1, 2, 0: @GUT.quals.Rep + + +package: +class Wildcards: +field l4: +type: +inner-type 3, 0: @GUT.quals.Rep + +package: +class Wildcards: +field l4: +type: +inner-type 3, 0, 2, 0: @GUT.quals.Peer + +package: +class Wildcards: +field l4: +type: +inner-type 3, 0, 2, 0, 3, 0: @GUT.quals.Rep + +package: +class Wildcards: +field l4: +type: +inner-type 3, 0, 2, 0, 3, 0, 2, 0: @GUT.quals.Peer + +package: +class Wildcards: +field l4: +type: +inner-type 3, 0, 2, 0, 3, 1: @GUT.quals.Rep + +package: +class Wildcards: +field l4: +type: +inner-type 3, 1: @GUT.quals.Peer
diff --git a/annotation-file-utilities/tests/source-extension/Wildcards.java b/annotation-file-utilities/tests/source-extension/Wildcards.java new file mode 100644 index 0000000..eda12f5 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/Wildcards.java
@@ -0,0 +1,10 @@ +import java.util.List; +import java.util.Map; + +public class Wildcards { + List<?> l1; + List<? extends Object> l2; + Map<?, ?> l3; + Map<? extends Map<?, String>, Object> l4; +} +
diff --git a/annotation-file-utilities/tests/source-extension/WildcardsSuper.goal b/annotation-file-utilities/tests/source-extension/WildcardsSuper.goal new file mode 100644 index 0000000..86e06c0 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/WildcardsSuper.goal
@@ -0,0 +1,6 @@ +import java.util.*; + +public class WildcardsSuper { + @GUT.quals.Rep + Enumeration<? super @GUT.quals.Peer WildcardsSuper> f; +}
diff --git a/annotation-file-utilities/tests/source-extension/WildcardsSuper.jaif b/annotation-file-utilities/tests/source-extension/WildcardsSuper.jaif new file mode 100644 index 0000000..c333c13 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/WildcardsSuper.jaif
@@ -0,0 +1,15 @@ +package GUT.quals: +annotation @Rep: +package GUT.quals: +annotation @Peer: + +package: +class WildcardsSuper: +field f: +type: +inner-type 3, 0, 2, 0: @GUT.quals.Peer + +package: +class WildcardsSuper: +field f: +type: @GUT.quals.Rep
diff --git a/annotation-file-utilities/tests/source-extension/WildcardsSuper.java b/annotation-file-utilities/tests/source-extension/WildcardsSuper.java new file mode 100644 index 0000000..7bb4ff0 --- /dev/null +++ b/annotation-file-utilities/tests/source-extension/WildcardsSuper.java
@@ -0,0 +1,5 @@ +import java.util.*; + +public class WildcardsSuper { + Enumeration<? super WildcardsSuper> f; +}
diff --git a/annotation-file-utilities/tests/system-test/Makefile b/annotation-file-utilities/tests/system-test/Makefile new file mode 100755 index 0000000..46c2533 --- /dev/null +++ b/annotation-file-utilities/tests/system-test/Makefile
@@ -0,0 +1,72 @@ +# System test of annotation tools + +# This test runs most of the AFU tools, on a single program +# source-ann/AnnotationTest.java that contains an annotation in every +# possible location. + +ANNCAT ?= ../../../scene-lib/anncat +JAVAVERSION = $(shell $(JAVA) -version 2>&1) +ifeq (1.7.,$(findstring 1.7.,$(JAVAVERSION))) + XJAVACTARGET = -source 7 -target 7 +endif + +#Switch the lines below to deactivate this test +all: clean results +#all: warn_and_exit + +warn_and_exit: + @echo "" + @echo "******************************************************" + @echo "Warning: The sytem test has been temporarily disabled!" + @echo "******************************************************" + @echo "" + +results: out1 check-out2 out3 out4.class check-out5 + +clean: + rm -rf out[1-5]* + + +# Step 1: Compile the source with annotations. +out1: + mkdir out1 + ${XJAVAC} ${XJAVACTARGET} -d out1 source-ann/AnnotationTest.java || (\rm -rf out1 && false) + +# Step 2: Convert the annotated class file to an annotation file. +# Do we get the right annotations? +out2.jaif: + CLASSPATH=`pwd`/out1 ${ANNCAT} --class out1/annotations/tests/AnnotationTest.class --out --index out2.jaif + +out2.diff: out2.jaif + diff -u expected-annos.jaif out2.jaif > out2.diff + +.PHONY: check-out2 +# Fail if out2.diff is non-empty +check-out2: out2.diff + [ ! -s out2.diff ] || (cat out2.diff && false) + +# Step 3: Compile the source without annotations. +out3: + mkdir out3 + ${XJAVAC} ${XJAVACTARGET} -d out3 source-plain/AnnotationTest.java || (\rm -rf out3 && false) + +# Step 4: Insert annotations into the class file. +# Can debug with: javap -v out4.class +out4.class: out3 + CLASSPATH=`pwd`/out1 ${ANNCAT} --index expected-annos.jaif --out --class out3/annotations/tests/AnnotationTest.class --to out4.class + +# Step 5: Convert the annotation-inserted class file to an annotation file. +# Do we get the right annotations? +# (The annotation-compiled and annotation-inserted class files tend to differ +# for stupid reasons (e.g., order of items in the constant pool), so we don't +# compare them.) +out5.jaif: out4.class + CLASSPATH=`pwd`/out1 ${ANNCAT} --class out4.class --out --index out5.jaif + +out5.diff: out5.jaif + diff -u expected-annos.jaif out5.jaif > out5.diff + +.PHONY: check-out5 +# Fail if out5.diff is non-empty +check-out5: out5.diff + [ ! -s out5.diff ] || (cat out5.diff && false)
diff --git a/annotation-file-utilities/tests/system-test/expected-annos-better-UNUSED.jaif b/annotation-file-utilities/tests/system-test/expected-annos-better-UNUSED.jaif new file mode 100644 index 0000000..e5f669f --- /dev/null +++ b/annotation-file-utilities/tests/system-test/expected-annos-better-UNUSED.jaif
@@ -0,0 +1,116 @@ +package annotations.tests: +annotation invisible @AClass: + +package annotations.tests: +annotation invisible @CClass: + +package annotations.tests: +annotation invisible @A0A: + +package annotations.tests: +annotation invisible @C0A: + +package annotations.tests: +annotation invisible @A0B: + +package annotations.tests: +annotation invisible @C0B: + +package annotations.tests: +annotation invisible @A06: + +package annotations.tests: +annotation invisible @C06: + +package annotations.tests: +annotation invisible @A0C: + +package annotations.tests: +annotation invisible @C0C: + +package annotations.tests: +annotation invisible @A0D: + +package annotations.tests: +annotation invisible @C0D: + +package annotations.tests: +annotation invisible @A08: + +package annotations.tests: +annotation invisible @C08: + +package annotations.tests: +annotation invisible @A09: + +package annotations.tests: +annotation invisible @C09: + +package annotations.tests: +annotation invisible @A00: + +package annotations.tests: +annotation invisible @C00: + +package annotations.tests: +annotation invisible @A01: + +package annotations.tests: +annotation invisible @C01: + +package annotations.tests: +annotation invisible @A02: + +package annotations.tests: +annotation invisible @C02: + +package annotations.tests: +annotation invisible @A04: + +package annotations.tests: +annotation invisible @C04: + +package annotations.tests: +annotation invisible @A05: + +package annotations.tests: +annotation invisible @C05: + +package annotations.tests: +annotation invisible @A0E: + +package annotations.tests: +annotation invisible @C0E: + +package annotations.tests: +annotation invisible @A0F: + +package annotations.tests: +annotation invisible @C0F: + +package annotations.tests: +class AnnotationTest: @annotations.tests.AClass @annotations.tests.CClass + bound 0 &0: @annotations.tests.A10 @annotations.tests.C10 + inner-type 0: @annotations.tests.A11 @annotations.tests.C11 + + field field: @annotations.tests.A0E @annotations.tests.C0E + inner-type 0: @annotations.tests.A0F @annotations.tests.C0F + + method <init>()V: + + method doSomething(Ljava/util/Set;)Ljava/util/HashSet;: + return: @annotations.tests.A0A @annotations.tests.C0A + inner-type 0: @annotations.tests.A0B @annotations.tests.C0B + bound 0 &0: @annotations.tests.A12 @annotations.tests.C12 + inner-type 0: @annotations.tests.A13 @annotations.tests.C13 + parameter #0: @annotations.tests.A0C @annotations.tests.C0C + inner-type 0: @annotations.tests.A0D @annotations.tests.C0D + receiver: @annotations.tests.A06 @annotations.tests.C06 + local 2 #23+2: @annotations.tests.A08 @annotations.tests.C08 + inner-type 0: @annotations.tests.A09 @annotations.tests.C09 + typecast #7: @annotations.tests.A00 @annotations.tests.C00 + inner-type 0: @annotations.tests.A01 @annotations.tests.C01 + instanceof #0: @annotations.tests.A02 @annotations.tests.C02 + new #15: @annotations.tests.A04 @annotations.tests.C04 + inner-type 0: @annotations.tests.A05 @annotations.tests.C05 +
diff --git a/annotation-file-utilities/tests/system-test/expected-annos.jaif b/annotation-file-utilities/tests/system-test/expected-annos.jaif new file mode 100644 index 0000000..5b43195 --- /dev/null +++ b/annotation-file-utilities/tests/system-test/expected-annos.jaif
@@ -0,0 +1,189 @@ +package annotations.tests: +annotation @AClass: + +package annotations.tests: +annotation @CClass: + +package annotations.tests: +annotation @A0A: @java.lang.annotation.Target(value={TYPE_USE,METHOD}) + +package annotations.tests: +annotation @C0A: @java.lang.annotation.Target(value={TYPE_USE,METHOD}) + +package annotations.tests: +annotation @A0AT: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C0AT: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A0B: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C0B: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A06: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER}) + +package annotations.tests: +annotation @C06: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER}) + +package annotations.tests: +annotation @A0C: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER}) + +package annotations.tests: +annotation @C0C: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER}) + +package annotations.tests: +annotation @A0D: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C0D: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A08: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C08: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A09: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C09: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A00: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C00: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A01: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C01: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A02: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C02: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A04: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C04: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A05: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C05: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A0E: @java.lang.annotation.Target(value={TYPE_USE,FIELD}) + +package annotations.tests: +annotation @C0E: @java.lang.annotation.Target(value={TYPE_USE,FIELD}) + +package annotations.tests: +annotation @A20: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C20: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A21: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C21: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A22: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C22: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A23: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C23: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A24: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C24: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A2B: + +package annotations.tests: +annotation @C2B: + +package annotations.tests: +annotation @A2C: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C2C: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A2D: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C2D: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A2E: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C2E: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @A2F: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +annotation @C2F: @java.lang.annotation.Target(value={TYPE_USE}) + +package annotations.tests: +class AnnotationTest: @annotations.tests.AClass @annotations.tests.CClass + + field field: @annotations.tests.A0E @annotations.tests.C0E + type: @annotations.tests.A0E @annotations.tests.C0E + inner-type 3, 0: @annotations.tests.A20 @annotations.tests.C20 + inner-type 3, 0,0, 0: @annotations.tests.A21 @annotations.tests.C21 + + field inner: + type: + inner-type 1, 0,1, 0: @annotations.tests.A22 @annotations.tests.C22 + inner-type 1, 0,1, 0,3, 0: @annotations.tests.A23 @annotations.tests.C23 + inner-type 1, 0: @annotations.tests.A24 @annotations.tests.C24 + + field entry: @annotations.tests.A2B @annotations.tests.C2B + type: @annotations.tests.A2C @annotations.tests.C2C + inner-type 3, 0: @annotations.tests.A2D @annotations.tests.C2D + inner-type 3, 1: @annotations.tests.A2E @annotations.tests.C2E + inner-type 3, 1,2, 0: @annotations.tests.A2F @annotations.tests.C2F + + method <init>()V: + return: + + method doSomething(Ljava/util/Set;)Ljava/util/HashSet;: @annotations.tests.A0A @annotations.tests.C0A + return: @annotations.tests.A0A @annotations.tests.C0A @annotations.tests.A0AT @annotations.tests.C0AT + inner-type 3, 0: @annotations.tests.A0B @annotations.tests.C0B + receiver: @annotations.tests.A06 @annotations.tests.C06 + parameter #0: @annotations.tests.A0C @annotations.tests.C0C + type: @annotations.tests.A0C @annotations.tests.C0C + inner-type 3, 0: @annotations.tests.A0D @annotations.tests.C0D + local 2 #23+2: @annotations.tests.A08 @annotations.tests.C08 + type: + inner-type 3, 0: @annotations.tests.A09 @annotations.tests.C09 + typecast #7: @annotations.tests.A00 @annotations.tests.C00 + inner-type 3, 0: @annotations.tests.A01 @annotations.tests.C01 + instanceof #0: @annotations.tests.A02 @annotations.tests.C02 + new #15: @annotations.tests.A04 @annotations.tests.C04 + inner-type 3, 0: @annotations.tests.A05 @annotations.tests.C05 +
diff --git a/annotation-file-utilities/tests/system-test/source-ann-better/AnnotationTest.java b/annotation-file-utilities/tests/system-test/source-ann-better/AnnotationTest.java new file mode 100644 index 0000000..13c337a --- /dev/null +++ b/annotation-file-utilities/tests/system-test/source-ann-better/AnnotationTest.java
@@ -0,0 +1,57 @@ +package annotations.tests; + +import java.util.*; + +@interface AClass {} +@interface A00 {} +@interface A01 {} +@interface A02 {} +@interface A04 {} +@interface A05 {} +@interface A06 {} +@interface A08 {} +@interface A09 {} +@interface A0A {} +@interface A0B {} +@interface A0C {} +@interface A0D {} +@interface A0E {} +@interface A0F {} +@interface A10 {} +@interface A11 {} +@interface A12 {} +@interface A13 {} +@interface CClass {} +@interface C00 {} +@interface C01 {} +@interface C02 {} +@interface C04 {} +@interface C05 {} +@interface C06 {} +@interface C08 {} +@interface C09 {} +@interface C0A {} +@interface C0B {} +@interface C0C {} +@interface C0D {} +@interface C0E {} +@interface C0F {} +@interface C10 {} +@interface C11 {} +@interface C12 {} +@interface C13 {} + +public @AClass /*@CClass*/ class AnnotationTest<Foo extends @A10 /*@C10*/ Comparable<@A11 /*@C11*/ Integer>> { + + @A0E /*@C0E*/ Iterable<@A0F /*@C0F*/ String> field; + + <Bar extends @A12 /*@C12*/ Comparable<@A13 /*@C13*/ Integer>> @A0A /*@C0A*/ HashSet<@A0B /*@C0B*/ Integer> + doSomething(@A06 AnnotationTest this, @A0C /*@C0C*/ Set<@A0D /*@C0D*/ Integer> param) /*@C06*/ { + @A08 /*@C08*/ HashSet<@A09 /*@C09*/ Integer> local; + if (param instanceof @A02 /*@C02*/ HashSet) + local = (@A00 /*@C00*/ HashSet<@A01 /*@C01*/ Integer>) param; + else + local = new @A04 /*@C04*/ HashSet<@A05 /*@C05*/ Integer>(); + return local; + } +}
diff --git a/annotation-file-utilities/tests/system-test/source-ann/AnnotationTest.java b/annotation-file-utilities/tests/system-test/source-ann/AnnotationTest.java new file mode 100644 index 0000000..6996920 --- /dev/null +++ b/annotation-file-utilities/tests/system-test/source-ann/AnnotationTest.java
@@ -0,0 +1,140 @@ +package annotations.tests; + +import java.util.*; +import java.lang.annotation.*; + +@interface AClass {} +@Target(ElementType.TYPE_USE) +@interface A00 {} +@Target(ElementType.TYPE_USE) +@interface A01 {} +@Target(ElementType.TYPE_USE) +@interface A02 {} +@Target(ElementType.TYPE_USE) +@interface A04 {} +@Target(ElementType.TYPE_USE) +@interface A05 {} + +@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) +@interface A06 {} +@Target(ElementType.TYPE_USE) +@interface A08 {} +@Target(ElementType.TYPE_USE) +@interface A09 {} + +@Target({ElementType.TYPE_USE, ElementType.METHOD}) +@interface A0A {} +@Target(ElementType.TYPE_USE) +@interface A0AT {} +@Target(ElementType.TYPE_USE) +@interface A0B {} + +@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) +@interface A0C {} +@Target(ElementType.TYPE_USE) +@interface A0D {} + +@Target({ElementType.TYPE_USE, ElementType.FIELD}) +@interface A0E {} +@Target({ElementType.TYPE_USE}) +@interface A21 {} + + +@Target(ElementType.TYPE_USE) +@interface A20 {} +@Target(ElementType.TYPE_USE) +@interface A22 {} +@Target(ElementType.TYPE_USE) +@interface A23 {} +@Target(ElementType.TYPE_USE) +@interface A24 {} +@interface A2B {} +@Target(ElementType.TYPE_USE) +@interface A2C {} +@Target(ElementType.TYPE_USE) +@interface A2D {} +@Target(ElementType.TYPE_USE) +@interface A2E {} +@Target(ElementType.TYPE_USE) +@interface A2F {} +@interface A10 {} +@interface A11 {} +@interface A12 {} +@interface A13 {} +@interface CClass {} +@Target(ElementType.TYPE_USE) +@interface C00 {} +@Target(ElementType.TYPE_USE) +@interface C01 {} +@Target(ElementType.TYPE_USE) +@interface C02 {} +@Target(ElementType.TYPE_USE) +@interface C04 {} +@Target(ElementType.TYPE_USE) +@interface C05 {} + +@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) +@interface C06 {} +@Target(ElementType.TYPE_USE) +@interface C08 {} +@Target(ElementType.TYPE_USE) +@interface C09 {} +@Target({ElementType.TYPE_USE, ElementType.METHOD}) +@interface C0A {} +@Target(ElementType.TYPE_USE) +@interface C0AT {} +@Target(ElementType.TYPE_USE) +@interface C0B {} + +@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) +@interface C0C {} +@Target(ElementType.TYPE_USE) +@interface C0D {} +@Target({ElementType.TYPE_USE, ElementType.FIELD}) +@interface C0E {} +@Target(ElementType.TYPE_USE) +@interface C20 {} +@Target(ElementType.TYPE_USE) +@interface C21 {} +@Target(ElementType.TYPE_USE) +@interface C22 {} +@Target(ElementType.TYPE_USE) +@interface C23 {} +@Target(ElementType.TYPE_USE) +@interface C24 {} +@interface C2B {} +@Target(ElementType.TYPE_USE) +@interface C2C {} +@Target(ElementType.TYPE_USE) +@interface C2D {} +@Target(ElementType.TYPE_USE) +@interface C2E {} +@Target(ElementType.TYPE_USE) +@interface C2F {} +@interface C10 {} +@interface C11 {} +@interface C12 {} +@interface C13 {} + +public @AClass /*@CClass*/ class AnnotationTest<Foo extends /*A10*/ /*C10*/ Comparable</*A11*/ /*C11*/ Integer>> { + class Outer { + class Inner<Baz> { + int baz(Baz o) { return o.hashCode() ^ this.hashCode(); } + } + } + + @A0E /*@C0E*/ Iterable<@A21 /*@C21*/ String @A20 /*@C20*/ []> field; + @A24 /*@C24*/ Outer.@A22 /*@C22*/ Inner<@A23 /*@C23*/ String> inner; + @A2B /*@C2B*/ Map.@A2C /*@C2C*/ Entry<@A2D /*@C2D*/ Integer, @A2E /*@C2E*/ ? extends @A2F /*@C2F*/ CharSequence> entry; + + // TODO: crash when A12, C12, A13, or C13 are annotations! + <Bar extends /*A12*/ /*C12*/ Comparable</*A13*/ /*C13*/ Integer>> @A0A /*@C0A*/ @A0AT /*@C0AT*/ HashSet<@A0B /*@C0B*/ Integer> + doSomething(@A06 /*@C06*/ AnnotationTest<Foo> this, @A0C /*@C0C*/ Set<@A0D /*@C0D*/ Integer> param) { + @A08 /*@C08*/ HashSet<@A09 /*@C09*/ Integer> local; + if (param instanceof @A02 /*@C02*/ HashSet) + local = (@A00 /*@C00*/ HashSet<@A01 /*@C01*/ Integer>) param; + else + local = new @A04 /*@C04*/ HashSet<@A05 /*@C05*/ Integer>(); + return local; + } +}
diff --git a/annotation-file-utilities/tests/system-test/source-plain/AnnotationTest.java b/annotation-file-utilities/tests/system-test/source-plain/AnnotationTest.java new file mode 100644 index 0000000..4f208bb --- /dev/null +++ b/annotation-file-utilities/tests/system-test/source-plain/AnnotationTest.java
@@ -0,0 +1,25 @@ +package annotations.tests; + +import java.util.*; + +public class AnnotationTest<Foo extends Comparable<Integer>> { + class Outer { + class Inner<Baz> { + int baz(Baz o) { return o.hashCode() ^ this.hashCode(); } + } + } + + Iterable<String[]> field; + Outer.Inner<String> inner; + Map.Entry<Integer, ? extends CharSequence> entry; + + <Bar extends Comparable<Integer>> HashSet<Integer> + doSomething(Set<Integer> param) { + HashSet<Integer> local; + if (param instanceof HashSet) + local = (HashSet<Integer>) param; + else + local = new HashSet<Integer>(); + return local; + } +}
diff --git a/asmx/.classpath b/asmx/.classpath new file mode 100644 index 0000000..e0a7d3a --- /dev/null +++ b/asmx/.classpath
@@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="lib" path="config/ow_util_ant_tasks.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/jsr308-langtools"/> + <classpathentry kind="output" path="bin"/> +</classpath>
diff --git a/asmx/.project b/asmx/.project new file mode 100644 index 0000000..8cc17c4 --- /dev/null +++ b/asmx/.project
@@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>asmx</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription>
diff --git a/asmx/bcel.jar b/asmx/bcel.jar new file mode 100644 index 0000000..2fa90ce --- /dev/null +++ b/asmx/bcel.jar Binary files differ
diff --git a/asmx/build.config b/asmx/build.config new file mode 100644 index 0000000..452caf8 --- /dev/null +++ b/asmx/build.config
@@ -0,0 +1,18 @@ +# Defines values for the 'build.properties' build properties +# This file is used to build the project in the case of bundled external jars + +objectweb.ant.tasks.path config/ow_util_ant_tasks.jar + +bcel.path test/lib/bcel-5.1.jar + +aspectj.path test/lib/aspectjweaver.jar + +serp.path test/lib/serp.jar + +javassist.path test/lib/javassist.jar + +janino.path test/lib/janino-2.3.4.jar + +cobertura.path test/lib/cobertura-1.8.jar;test/lib/jakarta-oro-2.0.8.jar;test/lib/log4j-1.2.9.jar;test/lib/asm-2.2.1.jar + +cobertura.runtime.path test/lib/cobertura-1.8.jar \ No newline at end of file
diff --git a/asmx/build.properties b/asmx/build.properties new file mode 100644 index 0000000..34d0c3f --- /dev/null +++ b/asmx/build.properties
@@ -0,0 +1,92 @@ +############################################################################### +#ASM: a very small and fast Java bytecode manipulation framework +#Copyright (c) 2000-2005 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. +############################################################################### + +# Some information about the product + +product.name asm +product.version 2.2.2 +# product.snapshot yes + +plugin.version 2.2.2 + + +############################################################################### +# BUILD +############################################################################### + +# Wich compiler do you want to use ? +# jikes is faster than javac but keeps line numbers in compiled classes, even +# with the -O option (javac removes line numbers and local variable names with +# this option, resulting in smaller classes) + +build.compiler modern + +# Build class path (classes needed to build the project) +# Class path for the ObjectWeb utility Ant tasks (version 1.2 or higher) +# See http://forge.objectweb.org/projects/monolog + +objectweb.ant.tasks.path ow_util_ant_tasks.jar + +############################################################################### +# TESTS (PERFORMANCE COMPARISONS WITH BCEL AND SERP) +############################################################################### + +# Class path for the BCEL library (version 5.0) +# See http://jakarta.apache.org/bcel + +bcel.path bcel.jar + +# Class path for the SERP library (version 1.4.2) +# See http://serp.sourceforge.net + +serp.path serp.jar + +# Class path for the Javassist library (version 2.6) +# See http://www.csg.is.titech.ac.jp/~chiba/javassist + +javassist.path javassist.jar + +# Class path for the Janino compiler (version 2.3.3) +# See http://www.janino.net/ + +janino.path janino.jar + +############################################################################### +# DOCUMENTATION +############################################################################### + +# URLs of external Javadocs (JDK) + +jdk.url http://java.sun.com/j2se/1.4.2/docs/api + + +workspace ${basedir}/../.. +global.build.properties ${basedir}/../global.build.properties +user.build.properties ${basedir}/../user.build.properties
diff --git a/asmx/build.xml b/asmx/build.xml new file mode 100644 index 0000000..d5dbc7e --- /dev/null +++ b/asmx/build.xml
@@ -0,0 +1,518 @@ +<!-- + ! ASM: a very small and fast Java bytecode manipulation framework + ! Copyright (c) 2000-2005 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. +--> + +<project name="ASM" default="compile"> + + <!-- ==================================== --> + <!-- ======== PROPERTY DEFINITION ======= --> + <!-- ==================================== --> + + <property file="${user.home}/asm-build.properties"/> + <property file="build.config"/> + <property file="build.properties"/> + <property file="${global.build.properties}"/> + <property file="${user.build.properties}"/> + + <property name="src" value="${basedir}/src"/> + <property name="test" value="${basedir}/test"/> + <property name="doc" value="${basedir}/doc"/> + <property name="jdoc" value="${basedir}/jdoc"/> + <property name="web" value="${basedir}/web"/> + <property name="examples" value="${basedir}/examples"/> + <property name="examples.common" value="${examples}/common"/> + <property name="config" value="${basedir}/config"/> + <property name="archive" value="${basedir}/archive"/> + + <property name="out" value="${basedir}/output"/> + <property name="out.build" value="${out}/build"/> + <property name="out.instr" value="${out}/instr"/> + <property name="out.dist" value="${out}/dist"/> + <property name="out.dist.lib" value="${out.dist}/lib"/> + <property name="out.dist.doc" value="${out.dist}/doc"/> + <property name="out.dist.jdoc" value="${out.dist.doc}/javadoc"/> + <property name="out.dist.examples" value="${out.dist}/examples"/> + <property name="out.dist.externals" value="${out.dist}/externals"/> + <property name="out.test" value="${out}/test"/> + <property name="out.tmp" value="${out}/tmp"/> + <property name="out.zip" value="${out}/zip"/> + + <target name="properties"> + <condition property="examples.exist"> + <available file="${examples}"/> + </condition> + + <condition property="web.exist"> + <available file="${web}/build.xml"/> + </condition> + + <condition property="paths.configured"> + <and> + <isset property="objectweb.ant.tasks.path"/> + </and> + </condition> + + <condition property="product.shrink"> + <not><isset property="product.noshrink"/></not> + </condition> + </target> + + <!-- ================================== --> + <!-- ======== INITIALIZATION ======= --> + <!-- ================================== --> + + <target name="check" unless="paths.configured"> + <echo message="The 'build.properties' file must be configured"/> + <fail/> + </target> + + <target name="init" depends="properties,check"> + + <path id="classpath"> + <pathelement location="${out.build}"/> + </path> + + <path id="cobertura.classpath"> + <pathelement path="${cobertura.path}"/> + </path> + + <taskdef name="multipleAnt" + classname="org.objectweb.util.ant.MultipleAnt" + classpath="${objectweb.ant.tasks.path}"/> + + <taskdef name="javadocMultipleLink" + classname="org.objectweb.util.ant.JavadocMultipleLink" + classpath="${objectweb.ant.tasks.path}"/> + + <taskdef name="multipleCopy" + classname="org.objectweb.util.ant.MultipleCopy" + classpath="${objectweb.ant.tasks.path}"/> + + <taskdef classpathref="cobertura.classpath" resource="tasks.properties"/> + </target> + + <!-- =================================== --> + <!-- ========== COMPILE ========== --> + <!-- =================================== --> + + <target name="compile-debug" depends="init"> + <mkdir dir="${out.tmp}"/> + <javac destdir="${out.tmp}" debug="on" includeantruntime="false"> + <classpath> + <pathelement location="${out.tmp}"/> + <pathelement path="${annotations-compiler}/dist/lib/javac.jar"/> + </classpath> + <src path="${src}"/> + <include name="**/*.java"/> + </javac> + </target> + + <target name="shrink" depends="compile-debug" if="product.shrink"> + <echo message="Shrinking"/> + <java classname="org.objectweb.asm.optimizer.Shrinker"> + <classpath> + <pathelement location="${out.tmp}"/> + </classpath> + <arg value="${src}/org/objectweb/asm/optimizer/shrink.properties"/> + <arg value="${out.tmp}"/> + <arg value="${out.build}"/> + </java> + </target> + + <target name="noshrink" depends="compile-debug" if="product.noshrink"> + <copy todir="${out.build}"> + <fileset dir="${out.tmp}"/> + </copy> + </target> + + <target name="bin" depends="compile"/> + + <!-- only one of shrink and noshrink will actually get executed, + depending on whether product.noshrink is set. + --> + <target name="compile" depends="compile-debug,shrink,noshrink"> + <copy todir="${basedir}/bin"> + <fileset dir="${out.build}"/> + </copy> + </target> + + <!-- =================================== --> + <!-- ========== TEST ========== --> + <!-- =================================== --> + + <target name="test" depends="compile"> + <condition property="classes" value="${out.build}"> + <not><isset property="debug"/></not> + </condition> + <condition property="classes" value="${out.tmp}"> + <isset property="debug"/> + </condition> + <condition property="asm.test" value="${java.home}/lib/rt.jar,${out.test}/cases"> + <not><isset property="asm.test"/></not> + </condition> + <ant antfile="${test}/build.xml" target="test" inheritRefs="true"/> + </target> + + <target name="test.report"> + <junitreport todir="${out.test}/reports"> + <fileset dir="${out.test}/reports"> + <include name="TEST-*.xml"/> + </fileset> + <report todir="${out.test}/reports"/> + </junitreport> + </target> + + <target name="coverage" depends="compile"> + <delete file="cobertura.ser"/> + <delete dir="${out.instr}"/> + <cobertura-instrument todir="${out.instr}"> + <ignore regex="java.lang.Error"/> + <fileset dir="${out.tmp}"> + <include name="**/*.class"/> + <exclude name="**/optimizer/*.class"/> + <exclude name="**/xml/Processor*.class"/> + <exclude name="**/*Test*.class" /> + </fileset> + </cobertura-instrument> + <copy todir="${out.instr}" preservelastmodified="yes"> + <fileset dir="${out.tmp}"/> + </copy> + <property name="coverage" value="yes"/> + <property name="classes" value="${out.instr}"/> + <property name="asm.test.class" value="pkg"/> + <condition property="asm.test" value="${out.test}/cases"> + <not><isset property="asm.test"/></not> + </condition> + <condition property="test.type" value="conform"> + <not><isset property="test.type"/></not> + </condition> + <ant antfile="${test}/build.xml" target="test" inheritRefs="true"/> + </target> + + <target name="coverage.check" depends="init"> + <cobertura-check branchrate="100" linerate="100"> + </cobertura-check> + </target> + + <target name="coverage.report" depends="init"> + <cobertura-report destdir="${out}/coverage" srcdir="${src}" format="xml"/> + <cobertura-report destdir="${out}/coverage"> + <fileset dir="${src}"> + <include name="**/*.java"/> + <exclude name="**/asm/optimizer/**/*.java"/> + </fileset> + </cobertura-report> + </target> + + <!-- =================================== --> + <!-- ========== DIST ========== --> + <!-- =================================== --> + + <target name="dist.init"> + <mkdir dir="${out.dist}"/> + <mkdir dir="${out.dist.doc}"/> + <mkdir dir="${out.dist.jdoc}"/> + <mkdir dir="${out.dist.lib}"/> + </target> + + <target name="dist.version"> + <tstamp> + <format property="product.build.time" pattern="yyyyMMdd.HHmmss"/> + </tstamp> + + <condition property="product.artifact" value="${product.version}"> + <not><isset property="product.snapshot"/></not> + </condition> + <condition property="product.artifact" value="${product.build.time}"> + <isset property="product.snapshot"/> + </condition> + + <condition property="plugin.artifact" value="${plugin.version}"> + <not><isset property="product.snapshot"/></not> + </condition> + <condition property="plugin.artifact" value="${plugin.version}.${product.build.time}"> + <isset property="product.snapshot"/> + </condition> + </target> + + <target name="jar" depends="dist.init,dist.version,compile,shrink"> + <multipleAnt dir="${archive}"/> + <java classname="org.objectweb.asm.optimizer.JarOptimizer"> + <classpath refid="classpath"/> + <arg value="${out.dist.lib}"/> + </java> + </target> + + <target name="jdoc" depends="init,dist.init"> + <copy todir="${out.dist.doc}" + preservelastmodified="yes" + includeEmptyDirs="false"> + <fileset dir="${doc}"> + <include name="**/*"/> + <exclude name="**/*.fig"/> + </fileset> + </copy> + <multipleAnt dir="${jdoc}"/> + </target> + + <target name="examples" depends="init,dist.init" if="examples.exist"> + <mkdir dir="${out.dist.examples}"/> + <copy todir="${out.dist.examples}" + preservelastmodified="yes" + includeEmptyDirs="yes"> + <fileset dir="${examples}"> + <exclude name="common"/> + <exclude name="common/**/*"/> + </fileset> + </copy> + + <multipleCopy file="${examples}/common/build.xml" + toDir="${out.dist.examples}" + notReplace="yes" + preservelastmodified="yes"> + <include name="*"/> + <exclude name="etc"/> + <exclude name="lib"/> + </multipleCopy> + + <copy toDir="${out.dist.examples}" preservelastmodified="yes"> + <fileset dir="${examples.common}"> + <include name="**/*"/> + <exclude name="build.xml"/> + </fileset> + </copy> + <replace dir="${out.dist.examples}/etc" token="@product.version@" value="${product.version}"/> + + <copy todir="${out.dist}"> + <fileset dir="."> + <include name="externals/*.jar"/> + </fileset> + </copy> + </target> + + <!-- + <target name="eclipse.generate" depends="dist.version"> + </target> + + <target name="eclipse.plugin" depends="jar,eclipse.generate"> + <jar zipfile="${out}/org.objectweb.asm_${plugin.artifact}.jar"> + <zipfileset dir="${basedir}"> + <include name="plugin.xml"/> + <include name="META-INF/MANIFEST.MF"/> + <include name="**/asm-${product.artifact}.jar"/> + <include name="**/asm-tree-${product.artifact}.jar"/> + <include name="**/asm-analysis-${product.artifact}.jar"/> + <include name="**/asm-util-${product.artifact}.jar"/> + <include name="**/asm-commons-${product.artifact}.jar"/> + <include name="**/asm-attrs-${product.artifact}.jar"/> + </zipfileset> + <manifest> + <attribute name="Bundle-ManifestVersion" value="2"/> + <attribute name="Bundle-Name" value="ASM Framework"/> + <attribute name="Bundle-SymbolicName" value="org.objectweb.asm;singleton:=true"/> + <attribute name="Bundle-Version" value="${plugin.artifact}"/> + <attribute name="Bundle-ClassPath" value="output/dist/lib/asm-${product.artifact}.jar, +output/dist/lib/asm-tree-${product.artifact}.jar, +output/dist/lib/asm-analysis-${product.artifact}.jar, +output/dist/lib/asm-commons-${product.artifact}.jar, +output/dist/lib/asm-attrs-${product.artifact}.jar, +output/dist/lib/asm-util-${product.artifact}.jar"/> + <attribute name="Bundle-Vendor" value="ObjectWeb.org"/> + <attribute name="Bundle-Localization" value="plugin"/> + <attribute name="Export-Package" value="org.objectweb.asm, +org.objectweb.asm.attrs, +org.objectweb.asm.commons, +org.objectweb.asm.signature, +org.objectweb.asm.tree, +org.objectweb.asm.tree.analysis, +org.objectweb.asm.util, +org.objectweb.asm.xml"/> + <attribute name="Eclipse-AutoStart" value="true"/> + </manifest> + </jar> + </target> + + <target name="eclipse.feature" depends="eclipse.plugin"> + <echo file="${out}/feature.xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?> +<feature id="org.objectweb.asm.feature" label="ASM Framework" + version="${plugin.artifact}" provider-name="ObjectWeb.org"> + + <description url="http://asm.objectweb.org/eclipse/asm/index.html"> +Feature contains ASM Java bytecode manipulation framework runtime. + </description> + + <copyright> +Copyright (c) 2000-2005 INRIA, France Telecom. +All rights reserved. + </copyright> + + <license> +Copyright (c) 2000-2005 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. + </license> + + <url> + <update label="ASM Framework" url="http://download.forge.objectweb.org/eclipse-update/site.xml"/> + </url> + + <plugin id="org.objectweb.asm" download-size="0" install-size="0" version="${plugin.artifact}"/> + +</feature>]]></echo> + + <jar jarfile="${out}/org.objectweb.asm.feature_${plugin.artifact}.jar"> + <fileset file="${out}/feature.xml"/> + </jar> + </target> + + <target name="eclipse.site" depends="eclipse.feature"> + <echo file="${out}/site.xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?> +<site> + <description url="http://asm.objectweb.org/eclipse/asm/index.html"> + ASM Framework + </description> + + <feature url="features/org.objectweb.asm.feature_${plugin.artifact}.jar" id="org.objectweb.asm.feature" version="${plugin.artifact}"> + <category name="asm"/> + </feature> + + <category-def name="asm" label="ASM Framework"/> + + <archive path="plugins/org.objectweb.asm_${plugin.artifact}.jar" url="http://download.forge.objectweb.org/asm/org.objectweb.asm_${plugin.artifact}.jar"/> +</site>]]></echo> + + <zip zipfile="${out}/org.objectweb.asm.update_${plugin.artifact}.zip"> + <zipfileset dir="${out}" includes="site.xml"/> + <zipfileset dir="${out}" includes="org.objectweb.asm.feature_${plugin.artifact}.jar" prefix="features"/> + <zipfileset dir="${out}" includes="org.objectweb.asm_${plugin.artifact}.jar" prefix="plugins"/> + </zip> + </target> + --> + + <target name="dist" depends="jar,jdoc,examples"> + <zip zipFile="${out.dist}/src.zip" basedir="${src}" excludes="**/optimizer/**/*"/> + </target> + + <!-- =================================== --> + <!-- ========== EXAMPLES ========== --> + <!-- =================================== --> + + <target name="example" depends="jar,examples"> + <ant inheritAll="false" + dir="${out.dist.examples}/${example.name}" + target="execute"/> + </target> + + <!-- =================================== --> + <!-- ========== ZIP ========== --> + <!-- =================================== --> + + <!-- creates zip files of the different distribution (source, binaries) --> + + <target name="zip" depends="dist"> + <mkdir dir="${out.zip}"/> + <tar destfile="${out.zip}/${product.name}-${product.version}.tar.gz" + compression="gzip"> + <tarfileset dir="${basedir}" prefix="${product.name}-${product.version}"> + <exclude name="build.config"/> + <exclude name="config/**"/> + <exclude name="config"/> + <exclude name="**/externals/**"/> + <exclude name="**/externals"/> + <exclude name="**/lib/**"/> + <exclude name="**/lib"/> + <exclude name="eclipse/**"/> + <exclude name="eclipse"/> + <exclude name="web/**"/> + <exclude name="web"/> + <exclude name="**/output/**"/> + <exclude name="**/output"/> + <exclude name="CVSROOT/**"/> + <exclude name="CVSROOT"/> + </tarfileset> + </tar> + <zip zipFile="${out.zip}/${product.name}-${product.version}-bin.zip"> + <zipfileset dir="${out.dist}" prefix="${product.name}-${product.version}"/> + </zip> + </target> + + <!-- =================================== --> + <!-- ========== CLEAN ========== --> + <!-- =================================== --> + + <!-- remove all directories --> + + <target name="clean.web" if="web.exist"> + <ant dir="${web}" target="clean"/> + </target> + + <target name="clean" depends="properties,clean.web"> + <delete dir="${basedir}/bin" /> + <delete dir="${out.build}"/> + <delete dir="${out.dist}"/> + <delete dir="${out.tmp}"/> + <delete dir="${out.zip}"/> + <delete dir="${out.test}"/> + <delete dir="${out.instr}"/> + </target> + + <!-- ==================================== --> + <!-- ========== HELP ========== --> + <!-- ==================================== --> + + <target name="help"> + <echo message="The available targets are the following:"/> + <echo message=" compile: compiles the product into ${out.build}"/> + <echo message=" dist: creates the product's distributions into ${out.dist}"/> + <echo message=" zip: creates the product's distributions zip files into ${out.zip}"/> + <echo message=" clean: removes all generated files."/> + <echo message=" jar: creates all jars in ${out.dist.lib}"/> + <echo message=" test: run all tests"/> + <echo message=""/> + <echo message="There are some options to run tests:"/> + <echo message=" -Dtest.group=<group name> Only a group of test: The default target of"/> + <echo message=" the xml file is called the test.group contains the xml file name with"/> + <echo message=" directory ex: ant -Dtest.group=conform/toto test => calls the default"/> + <echo message=" target of the file ${test}/conform/toto.xml"/> + <echo message=" -Dtest.type=<type name> Only a type of test: conform, deviance, stress,"/> + <echo message=" thread or perf. The test.type properties contains the directory name of"/> + <echo message=" the test type ex: ant -Dtest.type=conform test"/> + <echo message=" -Dtest.name=<test name> Only a single test. The target <test name> is called"/> + </target> + +</project>
diff --git a/asmx/config/README.txt b/asmx/config/README.txt new file mode 100644 index 0000000..4978bab --- /dev/null +++ b/asmx/config/README.txt
@@ -0,0 +1,2 @@ +This directory contains the external libraries required for building the +product, but which are not necessary to use it.
diff --git a/asmx/config/ow_util_ant_tasks.jar b/asmx/config/ow_util_ant_tasks.jar new file mode 100644 index 0000000..9d918a0 --- /dev/null +++ b/asmx/config/ow_util_ant_tasks.jar Binary files differ
diff --git a/asmx/core/asm-2.2.2.jar b/asmx/core/asm-2.2.2.jar new file mode 100644 index 0000000..fa779c2 --- /dev/null +++ b/asmx/core/asm-2.2.2.jar Binary files differ
diff --git a/asmx/core/org/objectweb/asm/AnnotationVisitor.class b/asmx/core/org/objectweb/asm/AnnotationVisitor.class new file mode 100644 index 0000000..51e5faf --- /dev/null +++ b/asmx/core/org/objectweb/asm/AnnotationVisitor.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/AnnotationWriter.class b/asmx/core/org/objectweb/asm/AnnotationWriter.class new file mode 100644 index 0000000..90934d5 --- /dev/null +++ b/asmx/core/org/objectweb/asm/AnnotationWriter.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Attribute.class b/asmx/core/org/objectweb/asm/Attribute.class new file mode 100644 index 0000000..210ff23 --- /dev/null +++ b/asmx/core/org/objectweb/asm/Attribute.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ByteVector.class b/asmx/core/org/objectweb/asm/ByteVector.class new file mode 100644 index 0000000..bd4ceb1 --- /dev/null +++ b/asmx/core/org/objectweb/asm/ByteVector.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassAdapter.class b/asmx/core/org/objectweb/asm/ClassAdapter.class new file mode 100644 index 0000000..209b538 --- /dev/null +++ b/asmx/core/org/objectweb/asm/ClassAdapter.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassReader.class b/asmx/core/org/objectweb/asm/ClassReader.class new file mode 100644 index 0000000..743a513 --- /dev/null +++ b/asmx/core/org/objectweb/asm/ClassReader.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassVisitor.class b/asmx/core/org/objectweb/asm/ClassVisitor.class new file mode 100644 index 0000000..7ca9b1d --- /dev/null +++ b/asmx/core/org/objectweb/asm/ClassVisitor.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassWriter.class b/asmx/core/org/objectweb/asm/ClassWriter.class new file mode 100644 index 0000000..35b2fb9 --- /dev/null +++ b/asmx/core/org/objectweb/asm/ClassWriter.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Edge.class b/asmx/core/org/objectweb/asm/Edge.class new file mode 100644 index 0000000..2e9d945 --- /dev/null +++ b/asmx/core/org/objectweb/asm/Edge.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/FieldVisitor.class b/asmx/core/org/objectweb/asm/FieldVisitor.class new file mode 100644 index 0000000..bc8c7a4 --- /dev/null +++ b/asmx/core/org/objectweb/asm/FieldVisitor.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/FieldWriter.class b/asmx/core/org/objectweb/asm/FieldWriter.class new file mode 100644 index 0000000..efddb42 --- /dev/null +++ b/asmx/core/org/objectweb/asm/FieldWriter.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Handler.class b/asmx/core/org/objectweb/asm/Handler.class new file mode 100644 index 0000000..59852b3 --- /dev/null +++ b/asmx/core/org/objectweb/asm/Handler.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Item.class b/asmx/core/org/objectweb/asm/Item.class new file mode 100644 index 0000000..dec3d57 --- /dev/null +++ b/asmx/core/org/objectweb/asm/Item.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Label.class b/asmx/core/org/objectweb/asm/Label.class new file mode 100644 index 0000000..5dbaaf5 --- /dev/null +++ b/asmx/core/org/objectweb/asm/Label.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/MethodAdapter.class b/asmx/core/org/objectweb/asm/MethodAdapter.class new file mode 100644 index 0000000..d64648a --- /dev/null +++ b/asmx/core/org/objectweb/asm/MethodAdapter.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/MethodVisitor.class b/asmx/core/org/objectweb/asm/MethodVisitor.class new file mode 100644 index 0000000..1f04a8b --- /dev/null +++ b/asmx/core/org/objectweb/asm/MethodVisitor.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/MethodWriter.class b/asmx/core/org/objectweb/asm/MethodWriter.class new file mode 100644 index 0000000..5bf49f4 --- /dev/null +++ b/asmx/core/org/objectweb/asm/MethodWriter.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Opcodes.class b/asmx/core/org/objectweb/asm/Opcodes.class new file mode 100644 index 0000000..00a7da6 --- /dev/null +++ b/asmx/core/org/objectweb/asm/Opcodes.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Type.class b/asmx/core/org/objectweb/asm/Type.class new file mode 100644 index 0000000..d8ef9b9 --- /dev/null +++ b/asmx/core/org/objectweb/asm/Type.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/signature/SignatureReader.class b/asmx/core/org/objectweb/asm/signature/SignatureReader.class new file mode 100644 index 0000000..9f433df --- /dev/null +++ b/asmx/core/org/objectweb/asm/signature/SignatureReader.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/signature/SignatureVisitor.class b/asmx/core/org/objectweb/asm/signature/SignatureVisitor.class new file mode 100644 index 0000000..1319d12 --- /dev/null +++ b/asmx/core/org/objectweb/asm/signature/SignatureVisitor.class Binary files differ
diff --git a/asmx/core/org/objectweb/asm/signature/SignatureWriter.class b/asmx/core/org/objectweb/asm/signature/SignatureWriter.class new file mode 100644 index 0000000..4a95194 --- /dev/null +++ b/asmx/core/org/objectweb/asm/signature/SignatureWriter.class Binary files differ
diff --git a/asmx/janino.jar b/asmx/janino.jar new file mode 100644 index 0000000..7874a29 --- /dev/null +++ b/asmx/janino.jar Binary files differ
diff --git a/asmx/javassist.jar b/asmx/javassist.jar new file mode 100644 index 0000000..1672012 --- /dev/null +++ b/asmx/javassist.jar Binary files differ
diff --git a/asmx/ow_util_ant_tasks.jar b/asmx/ow_util_ant_tasks.jar new file mode 100644 index 0000000..4b5dc8e --- /dev/null +++ b/asmx/ow_util_ant_tasks.jar Binary files differ
diff --git a/asmx/serp.jar b/asmx/serp.jar new file mode 100644 index 0000000..144667e --- /dev/null +++ b/asmx/serp.jar Binary files differ
diff --git a/asmx/src/org/objectweb/asm/AnnotationVisitor.java b/asmx/src/org/objectweb/asm/AnnotationVisitor.java new file mode 100644 index 0000000..8226ae8 --- /dev/null +++ b/asmx/src/org/objectweb/asm/AnnotationVisitor.java
@@ -0,0 +1,88 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A visitor to visit a Java annotation. The methods of this interface must be + * called in the following order: (<tt>visit<tt> | <tt>visitEnum<tt> | + * <tt>visitAnnotation<tt> | <tt>visitArray<tt>)* <tt>visitEnd<tt>. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public interface AnnotationVisitor { + + /** + * Visits a primitive value of the annotation. + * + * @param name the value name. + * @param value the actual value, whose type must be {@link Byte}, + * {@link Boolean}, {@link Character}, {@link Short}, + * {@link Integer}, {@link Long}, {@link Float}, {@link Double}, + * {@link String} or {@link Type}. + */ + void visit(String name, Object value); + + /** + * 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. + */ + void visitEnum(String name, String desc, String value); + + /** + * Visits a nested annotation value of the annotation. + * + * @param name the value name. + * @param desc the class descriptor of the nested annotation class. + * @return a non null visitor to visit the actual nested annotation value. + * <i>The nested annotation value must be fully visited before + * calling other methods on this annotation visitor</i>. + */ + AnnotationVisitor visitAnnotation(String name, String desc); + + /** + * Visits an array value of the annotation. + * + * @param name the value name. + * @return a non null visitor to visit the actual array value elements. The + * 'name' parameters passed to the methods of this visitor are + * ignored. <i>All the array values must be visited before calling + * other methods on this annotation visitor</i>. + */ + AnnotationVisitor visitArray(String name); + + /** + * Visits the end of the annotation. + */ + void visitEnd(); +}
diff --git a/asmx/src/org/objectweb/asm/AnnotationWriter.java b/asmx/src/org/objectweb/asm/AnnotationWriter.java new file mode 100644 index 0000000..918e234 --- /dev/null +++ b/asmx/src/org/objectweb/asm/AnnotationWriter.java
@@ -0,0 +1,311 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An {@link AnnotationVisitor} that generates annotations in bytecode form. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +final class AnnotationWriter implements AnnotationVisitor { + + /** + * The class writer to which this annotation must be added. + */ + private final ClassWriter cw; + + /** + * The number of values in this annotation. + */ + private int size; + + /** + * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation + * writers used for annotation default and annotation arrays use unnamed + * values. + */ + private final boolean named; + + /** + * The annotation values in bytecode form. This byte vector only contains + * the values themselves, i.e. the number of values must be stored as a + * unsigned short just before these bytes. + */ + private final ByteVector bv; + + /** + * The byte vector to be used to store the number of values of this + * annotation. See {@link #bv}. + */ + private final ByteVector parent; + + /** + * Where the number of values of this annotation must be stored in + * {@link #parent}. + */ + private final int offset; + + /** + * Next annotation writer. This field is used to store annotation lists. + */ + AnnotationWriter next; + + /** + * Previous annotation writer. This field is used to store annotation lists. + */ + AnnotationWriter prev; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link AnnotationWriter}. + * + * @param cw the class writer to which this annotation must be added. + * @param named <tt>true<tt> if values are named, <tt>false</tt> otherwise. + * @param bv where the annotation values must be stored. + * @param parent where the number of annotation values must be stored. + * @param offset where in <tt>parent</tt> the number of annotation values must + * be stored. + */ + AnnotationWriter( + final ClassWriter cw, + final boolean named, + final ByteVector bv, + final ByteVector parent, + final int offset) + { + this.cw = cw; + this.named = named; + this.bv = bv; + this.parent = parent; + this.offset = offset; + } + + // ------------------------------------------------------------------------ + // Implementation of the AnnotationVisitor interface + // ------------------------------------------------------------------------ + + public void visit(final String name, final Object value) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + if (value instanceof String) { + bv.put12('s', cw.newUTF8((String) value)); + } else if (value instanceof Byte) { + bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); + } else if (value instanceof Boolean) { + int v = ((Boolean) value).booleanValue() ? 1 : 0; + bv.put12('Z', cw.newInteger(v).index); + } else if (value instanceof Character) { + bv.put12('C', cw.newInteger(((Character) value).charValue()).index); + } else if (value instanceof Short) { + bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); + } else if (value instanceof Type) { + bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); + } else if (value instanceof byte[]) { + byte[] v = (byte[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('B', cw.newInteger(v[i]).index); + } + } else if (value instanceof boolean[]) { + boolean[] v = (boolean[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); + } + } else if (value instanceof short[]) { + short[] v = (short[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('S', cw.newInteger(v[i]).index); + } + } else if (value instanceof char[]) { + char[] v = (char[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('C', cw.newInteger(v[i]).index); + } + } else if (value instanceof int[]) { + int[] v = (int[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('I', cw.newInteger(v[i]).index); + } + } else if (value instanceof long[]) { + long[] v = (long[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('J', cw.newLong(v[i]).index); + } + } else if (value instanceof float[]) { + float[] v = (float[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('F', cw.newFloat(v[i]).index); + } + } else if (value instanceof double[]) { + double[] v = (double[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('D', cw.newDouble(v[i]).index); + } + } else { + Item i = cw.newConstItem(value); + bv.put12(".s.IFJDCS".charAt(i.type), i.index); + } + } + + public void visitEnum( + final String name, + final String desc, + final String value) + { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); + } + + public AnnotationVisitor visitAnnotation( + final String name, + final String desc) + { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + // write tag and type, and reserve space for values count + bv.put12('@', cw.newUTF8(desc)).putShort(0); + return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); + } + + public AnnotationVisitor visitArray(final String name) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + // write tag, and reserve space for array size + bv.put12('[', 0); + return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); + } + + public void visitEnd() { + if (parent != null) { + byte[] data = parent.data; + data[offset] = (byte) (size >>> 8); + data[offset + 1] = (byte) size; + } + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Returns the size of this annotation writer list. + * + * @return the size of this annotation writer list. + */ + int getSize() { + int size = 0; + AnnotationWriter aw = this; + while (aw != null) { + size += aw.bv.length; + aw = aw.next; + } + return size; + } + + /** + * Puts the annotations of this annotation writer list into the given byte + * vector. + * + * @param out where the annotations must be put. + */ + void put(final ByteVector out) { + int n = 0; + int size = 2; + AnnotationWriter aw = this; + AnnotationWriter last = null; + while (aw != null) { + ++n; + size += aw.bv.length; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putInt(size); + out.putShort(n); + aw = last; + while (aw != null) { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } + } + + /** + * Puts the given annotation lists into the given byte vector. + * + * @param panns an array of annotation writer lists. + * @param out where the annotations must be put. + */ + static void put(final AnnotationWriter[] panns, final ByteVector out) { + int size = 1 + 2 * panns.length; + for (int i = 0; i < panns.length; ++i) { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + out.putInt(size).putByte(panns.length); + for (int i = 0; i < panns.length; ++i) { + AnnotationWriter aw = panns[i]; + AnnotationWriter last = null; + int n = 0; + while (aw != null) { + ++n; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putShort(n); + aw = last; + while (aw != null) { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } + } + } +}
diff --git a/asmx/src/org/objectweb/asm/Attribute.java b/asmx/src/org/objectweb/asm/Attribute.java new file mode 100644 index 0000000..c55eb93 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Attribute.java
@@ -0,0 +1,254 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A non standard class, field, method or code attribute. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class Attribute { + + /** + * The type of this attribute. + */ + public final String type; + + /** + * The raw value of this attribute, used only for unknown attributes. + */ + byte[] value; + + /** + * The next attribute in this attribute list. May be <tt>null</tt>. + */ + Attribute next; + + /** + * Constructs a new empty attribute. + * + * @param type the type of the attribute. + */ + protected Attribute(final String type) { + this.type = type; + } + + /** + * Returns <tt>true</tt> if this type of attribute is unknown. The default + * implementation of this method always returns <tt>true</tt>. + * + * @return <tt>true</tt> if this type of attribute is unknown. + */ + public boolean isUnknown() { + return true; + } + + /** + * Returns <tt>true</tt> if this type of attribute is a code attribute. + * + * @return <tt>true</tt> if this type of attribute is a code attribute. + */ + public boolean isCodeAttribute() { + return false; + } + + /** + * Returns the labels corresponding to this attribute. + * + * @return the labels corresponding to this attribute, or <tt>null</tt> if + * this attribute is not a code attribute that contains labels. + */ + protected Label[] getLabels() { + return null; + } + + /** + * Reads a {@link #type type} attribute. This method must return a <i>new</i> + * {@link Attribute} object, of type {@link #type type}, corresponding to + * the <tt>len</tt> bytes starting at the given offset, in the given class + * reader. + * + * @param cr the class that contains the attribute to be read. + * @param off index of the first byte of the attribute's content in {@link + * ClassReader#b cr.b}. The 6 attribute header bytes, containing the + * type and the length of the attribute, are not taken into account + * here. + * @param len the length of the attribute's content. + * @param buf buffer to be used to call + * {@link ClassReader#readUTF8 readUTF8}, + * {@link ClassReader#readClass(int,char[]) readClass} or + * {@link ClassReader#readConst readConst}. + * @param codeOff index of the first byte of code's attribute content in + * {@link ClassReader#b cr.b}, or -1 if the attribute to be read is + * not a code attribute. The 6 attribute header bytes, containing the + * type and the length of the attribute, are not taken into account + * here. + * @param labels the labels of the method's code, or <tt>null</tt> if the + * attribute to be read is not a code attribute. + * @return a <i>new</i> {@link Attribute} object corresponding to the given + * bytes. + */ + protected Attribute read( + ClassReader cr, + int off, + int len, + char[] buf, + int codeOff, + Label[] labels) + { + Attribute attr = new Attribute(type); + attr.value = new byte[len]; + System.arraycopy(cr.b, off, attr.value, 0, len); + return attr; + } + + /** + * Returns the byte array form of this attribute. + * + * @param cw the class to which this attribute must be added. This parameter + * can be used to add to the constant pool of this class the items + * that corresponds to this attribute. + * @param code the bytecode of the method corresponding to this code + * attribute, or <tt>null</tt> if this attribute is not a code + * attributes. + * @param len the length of the bytecode of the method corresponding to this + * code attribute, or <tt>null</tt> if this attribute is not a code + * attribute. + * @param maxStack the maximum stack size of the method corresponding to + * this code attribute, or -1 if this attribute is not a code + * attribute. + * @param maxLocals the maximum number of local variables of the method + * corresponding to this code attribute, or -1 if this attribute is + * not a code attribute. + * @return the byte array form of this attribute. + */ + protected ByteVector write( + ClassWriter cw, + byte[] code, + int len, + int maxStack, + int maxLocals) + { + ByteVector v = new ByteVector(); + v.data = value; + v.length = value.length; + return v; + } + + /** + * Returns the length of the attribute list that begins with this attribute. + * + * @return the length of the attribute list that begins with this attribute. + */ + final int getCount() { + int count = 0; + Attribute attr = this; + while (attr != null) { + count += 1; + attr = attr.next; + } + return count; + } + + /** + * Returns the size of all the attributes in this attribute list. + * + * @param cw the class writer to be used to convert the attributes into byte + * arrays, with the {@link #write write} method. + * @param code the bytecode of the method corresponding to these code + * attributes, or <tt>null</tt> if these attributes are not code + * attributes. + * @param len the length of the bytecode of the method corresponding to + * these code attributes, or <tt>null</tt> if these attributes are + * not code attributes. + * @param maxStack the maximum stack size of the method corresponding to + * these code attributes, or -1 if these attributes are not code + * attributes. + * @param maxLocals the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these attributes + * are not code attributes. + * @return the size of all the attributes in this attribute list. This size + * includes the size of the attribute headers. + */ + final int getSize( + final ClassWriter cw, + final byte[] code, + final int len, + final int maxStack, + final int maxLocals) + { + Attribute attr = this; + int size = 0; + while (attr != null) { + cw.newUTF8(attr.type); + size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; + attr = attr.next; + } + return size; + } + + /** + * Writes all the attributes of this attribute list in the given byte + * vector. + * + * @param cw the class writer to be used to convert the attributes into byte + * arrays, with the {@link #write write} method. + * @param code the bytecode of the method corresponding to these code + * attributes, or <tt>null</tt> if these attributes are not code + * attributes. + * @param len the length of the bytecode of the method corresponding to + * these code attributes, or <tt>null</tt> if these attributes are + * not code attributes. + * @param maxStack the maximum stack size of the method corresponding to + * these code attributes, or -1 if these attributes are not code + * attributes. + * @param maxLocals the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these attributes + * are not code attributes. + * @param out where the attributes must be written. + */ + final void put( + final ClassWriter cw, + final byte[] code, + final int len, + final int maxStack, + final int maxLocals, + final ByteVector out) + { + Attribute attr = this; + while (attr != null) { + ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); + out.putShort(cw.newUTF8(attr.type)).putInt(b.length); + out.putByteArray(b.data, 0, b.length); + attr = attr.next; + } + } +}
diff --git a/asmx/src/org/objectweb/asm/ByteVector.java b/asmx/src/org/objectweb/asm/ByteVector.java new file mode 100644 index 0000000..270778b --- /dev/null +++ b/asmx/src/org/objectweb/asm/ByteVector.java
@@ -0,0 +1,293 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A dynamically extensible vector of bytes. This class is roughly equivalent to + * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. + * + * @author Eric Bruneton + */ +public class ByteVector { + + /** + * The content of this vector. + */ + byte[] data; + + /** + * Actual number of bytes in this vector. + */ + int length; + + /** + * Constructs a new {@link ByteVector ByteVector} with a default initial + * size. + */ + public ByteVector() { + data = new byte[64]; + } + + /** + * Constructs a new {@link ByteVector ByteVector} with the given initial + * size. + * + * @param initialSize the initial size of the byte vector to be constructed. + */ + public ByteVector(final int initialSize) { + data = new byte[initialSize]; + } + + /** + * Puts a byte into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b a byte. + * @return this byte vector. + */ + public ByteVector putByte(final int b) { + int length = this.length; + if (length + 1 > data.length) { + enlarge(1); + } + data[length++] = (byte) b; + this.length = length; + return this; + } + + /** + * Puts two bytes into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b1 a byte. + * @param b2 another byte. + * @return this byte vector. + */ + ByteVector put11(final int b1, final int b2) { + int length = this.length; + if (length + 2 > data.length) { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte) b1; + data[length++] = (byte) b2; + this.length = length; + return this; + } + + /** + * Puts a short into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param s a short. + * @return this byte vector. + */ + public ByteVector putShort(final int s) { + int length = this.length; + if (length + 2 > data.length) { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte) (s >>> 8); + data[length++] = (byte) s; + this.length = length; + return this; + } + + /** + * Puts a byte and a short into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b a byte. + * @param s a short. + * @return this byte vector. + */ + ByteVector put12(final int b, final int s) { + int length = this.length; + if (length + 3 > data.length) { + enlarge(3); + } + byte[] data = this.data; + data[length++] = (byte) b; + data[length++] = (byte) (s >>> 8); + data[length++] = (byte) s; + this.length = length; + return this; + } + + /** + * Puts an int into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param i an int. + * @return this byte vector. + */ + public ByteVector putInt(final int i) { + int length = this.length; + if (length + 4 > data.length) { + enlarge(4); + } + byte[] data = this.data; + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + this.length = length; + return this; + } + + /** + * Puts a long into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param l a long. + * @return this byte vector. + */ + public ByteVector putLong(final long l) { + int length = this.length; + if (length + 8 > data.length) { + enlarge(8); + } + byte[] data = this.data; + int i = (int) (l >>> 32); + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + i = (int) l; + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + this.length = length; + return this; + } + + /** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param s a String. + * @return this byte vector. + */ + public ByteVector putUTF8(final String s) { + int charLength = s.length(); + if (length + 2 + charLength > data.length) { + enlarge(2 + charLength); + } + int len = length; + byte[] data = this.data; + // optimistic algorithm: instead of computing the byte length and then + // serializing the string (which requires two loops), we assume the byte + // length is equal to char length (which is the most frequent case), and + // we start serializing the string right away. During the serialization, + // if we find that this assumption is wrong, we continue with the + // general method. + data[len++] = (byte) (charLength >>> 8); + data[len++] = (byte) (charLength); + for (int i = 0; i < charLength; ++i) { + char c = s.charAt(i); + 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; + } + } + 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 this; + } + + /** + * Puts an array of bytes into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b an array of bytes. May be <tt>null</tt> to put <tt>len</tt> + * null bytes into this byte vector. + * @param off index of the fist byte of b that must be copied. + * @param len number of bytes of b that must be copied. + * @return this byte vector. + */ + public ByteVector putByteArray(final byte[] b, final int off, final int len) + { + if (length + len > data.length) { + enlarge(len); + } + if (b != null) { + System.arraycopy(b, off, data, length, len); + } + length += len; + return this; + } + + /** + * Enlarge this byte vector so that it can receive n more bytes. + * + * @param size number of additional bytes that this byte vector should be + * able to receive. + */ + private void enlarge(final int size) { + int length1 = 2 * data.length; + int length2 = length + size; + byte[] newData = new byte[length1 > length2 ? length1 : length2]; + System.arraycopy(data, 0, newData, 0, length); + data = newData; + } +}
diff --git a/asmx/src/org/objectweb/asm/ClassAdapter.java b/asmx/src/org/objectweb/asm/ClassAdapter.java new file mode 100644 index 0000000..d9024d7 --- /dev/null +++ b/asmx/src/org/objectweb/asm/ClassAdapter.java
@@ -0,0 +1,130 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An empty {@link ClassVisitor} that delegates to another {@link ClassVisitor}. + * This class can be used as a super class to quickly implement useful class + * adapter classes, just by overriding the necessary methods. + * + * @author Eric Bruneton + */ +public class ClassAdapter implements ClassVisitor { + + /** + * The {@link ClassVisitor} to which this adapter delegates calls. + */ + protected ClassVisitor cv; + + /** + * Constructs a new {@link ClassAdapter} object. + * + * @param cv the class visitor to which this adapter must delegate calls. + */ + public ClassAdapter(final ClassVisitor cv) { + this.cv = cv; + } + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + cv.visit(version, access, name, signature, superName, interfaces); + } + + public void visitSource(final String source, final String debug) { + cv.visitSource(source, debug); + } + + public void visitOuterClass( + final String owner, + final String name, + final String desc) + { + cv.visitOuterClass(owner, name, desc); + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + return cv.visitAnnotation(desc, visible); + } + + //jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + return cv.visitTypeAnnotation(desc, visible, inCode); + } + // end jaime + + public void visitAttribute(final Attribute attr) { + cv.visitAttribute(attr); + } + + public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access) + { + cv.visitInnerClass(name, outerName, innerName, access); + } + + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + return cv.visitField(access, name, desc, signature, value); + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + return cv.visitMethod(access, name, desc, signature, exceptions); + } + + public void visitEnd() { + cv.visitEnd(); + } +}
diff --git a/asmx/src/org/objectweb/asm/ClassReader.java b/asmx/src/org/objectweb/asm/ClassReader.java new file mode 100644 index 0000000..cf6595e --- /dev/null +++ b/asmx/src/org/objectweb/asm/ClassReader.java
@@ -0,0 +1,2146 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import com.sun.tools.javac.code.TargetType; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * A Java class parser to make a {@link ClassVisitor} visit an existing class. + * This class parses a byte array conforming to the Java class file format and + * calls the appropriate visit methods of a given class visitor for each field, + * method and bytecode instruction encountered. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class ClassReader { + + /** + * True to enable signatures support. + */ + static final boolean SIGNATURES = true; + + /** + * True to enable annotations support. + */ + static final boolean ANNOTATIONS = true; + + /** + * True to enable stack map frames support. + */ + static final boolean FRAMES = true; + + /** + * True to enable bytecode writing support. + */ + static final boolean WRITER = true; + + /** + * True to enable JSR_W and GOTO_W support. + */ + static final boolean RESIZE = true; + + /** + * Flag to skip method code. If this class is set <code>CODE</code> + * attribute won't be visited. This can be used, for example, to retrieve + * annotations for methods and method parameters. + */ + public static final int SKIP_CODE = 1; + + /** + * Flag to skip the debug information in the class. If this flag is set the + * debug information of the class is not visited, i.e. the + * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and + * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be + * called. + */ + public static final int SKIP_DEBUG = 2; + + /** + * Flag to skip the stack map frames in the class. If this flag is set the + * stack map frames of the class is not visited, i.e. the + * {@link MethodVisitor#visitFrame visitFrame} method will not be called. + * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is + * used: it avoids visiting frames that will be ignored and recomputed from + * scratch in the class writer. + */ + public static final int SKIP_FRAMES = 4; + + /** + * Flag to expand the stack map frames. By default stack map frames are + * visited in their original format (i.e. "expanded" for classes whose + * version is less than V1_6, and "compressed" for the other classes). If + * this flag is set, stack map frames are always visited in expanded format + * (this option adds a decompression/recompression step in ClassReader and + * ClassWriter which degrades performances quite a lot). + */ + public static final int EXPAND_FRAMES = 8; + + /** + * The class to be parsed. <i>The content of this array must not be + * modified. This field is intended for {@link Attribute} sub classes, and + * is normally not needed by class generators or adapters.</i> + */ + public final byte[] b; + + /** + * The start index of each constant pool item in {@link #b b}, plus one. + * The one byte offset skips the constant pool item tag that indicates its + * type. + */ + private final int[] items; + + /** + * The String objects corresponding to the CONSTANT_Utf8 items. This cache + * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, + * which GREATLY improves performance (by a factor 2 to 3). This caching + * strategy could be extended to all constant pool items, but its benefit + * would not be so great for these items (because they are much less + * expensive to parse than CONSTANT_Utf8 items). + */ + private final String[] strings; + + /** + * Maximum length of the strings contained in the constant pool of the + * class. + */ + private final int maxStringLength; + + /** + * Start index of the class header information (access, name...) in + * {@link #b b}. + */ + public final int header; + + /** + * The start index of each bootstrap method. + */ + int[] bootstrapMethods; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link ClassReader} object. + * + * @param b the bytecode of the class to be read. + */ + public ClassReader(final byte[] b) { + this(b, 0, b.length); + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param b the bytecode of the class to be read. + * @param off the start offset of the class data. + * @param len the length of the class data. + */ + public ClassReader(final byte[] b, final int off, final int len) { + this.b = b; + // parses the constant pool + items = new int[readUnsignedShort(off + 8)]; + int ll = items.length; + strings = new String[ll]; + int max = 0; + int index = off + 10; + for (int i = 1; i < ll; ++i) { + items[i] = index + 1; + int tag = b[index]; + int size; + switch (tag) { + case ClassWriter.FIELD: + case ClassWriter.METH: + case ClassWriter.IMETH: + case ClassWriter.INT: + case ClassWriter.FLOAT: + case ClassWriter.NAME_TYPE: + case ClassWriter.INDY: + size = 5; + break; + case ClassWriter.LONG: + case ClassWriter.DOUBLE: + size = 9; + ++i; + break; + case ClassWriter.UTF8: + size = 3 + readUnsignedShort(index + 1); + if (size > max) { + max = size; + } + break; + case ClassWriter.HANDLE: + size = 4; + break; + // case ClassWriter.CLASS: + // case ClassWriter.STR: + // case ClassWriter.MTYPE + default: + size = 3; + break; + } + index += size; + } + maxStringLength = max; + // the class header information starts just after the constant pool + header = index; + } + + /** + * Copies the constant pool data into the given {@link ClassWriter}. Should + * be called before the {@link #accept(ClassVisitor,boolean)} method. + * + * @param classWriter the {@link ClassWriter} to copy constant pool into. + */ + void copyPool(final ClassWriter classWriter) { + char[] buf = new char[maxStringLength]; + int ll = items.length; + Item[] items2 = new Item[ll]; + for (int i = 1; i < ll; i++) { + int index = items[i]; + int tag = b[index - 1]; + Item item = new Item(i); + int nameType; + switch (tag) { + case ClassWriter.FIELD: + case ClassWriter.METH: + case ClassWriter.IMETH: + nameType = items[readUnsignedShort(index + 2)]; + item.set(tag, + readClass(index, buf), + readUTF8(nameType, buf), + readUTF8(nameType + 2, buf)); + break; + + case ClassWriter.INT: + item.set(readInt(index)); + break; + + case ClassWriter.FLOAT: + item.set(Float.intBitsToFloat(readInt(index))); + break; + + case ClassWriter.NAME_TYPE: + item.set(tag, + readUTF8(index, buf), + readUTF8(index + 2, buf), + null); + break; + + case ClassWriter.LONG: + item.set(readLong(index)); + ++i; + break; + + case ClassWriter.DOUBLE: + item.set(Double.longBitsToDouble(readLong(index))); + ++i; + break; + + case ClassWriter.UTF8: { + String s = strings[i]; + if (s == null) { + index = items[i]; + s = strings[i] = readUTF(index + 2, + readUnsignedShort(index), + buf); + } + item.set(tag, s, null, null); + break; + } + case ClassWriter.HANDLE: { + int fieldOrMethodRef = items[readUnsignedShort(index + 1)]; + nameType = items[readUnsignedShort(fieldOrMethodRef + 2)]; + item.set(ClassWriter.HANDLE_BASE + readByte(index), + readClass(fieldOrMethodRef, buf), + readUTF8(nameType, buf), + readUTF8(nameType + 2, buf)); + break; + } + case ClassWriter.INDY: + if (classWriter.bootstrapMethods == null) { + copyBootstrapMethods(classWriter, items2, buf); + } + nameType = items[readUnsignedShort(index + 2)]; + item.set(readUTF8(nameType, buf), + readUTF8(nameType + 2, buf), + readUnsignedShort(index)); + break; + // case ClassWriter.STR: + // case ClassWriter.CLASS: + // case ClassWriter.MTYPE + default: + item.set(tag, readUTF8(index, buf), null, null); + break; + } + + int index2 = item.hashCode % items2.length; + item.next = items2[index2]; + items2[index2] = item; + } + + int off = items[1] - 1; + classWriter.pool.putByteArray(b, off, header - off); + classWriter.items = items2; + classWriter.threshold = (int) (0.75d * ll); + classWriter.index = ll; + } + + /** + * Copies the bootstrap method data into the given {@link ClassWriter}. + * Should be called before the {@link #accept(ClassVisitor,int)} method. + * + * @param classWriter + * the {@link ClassWriter} to copy bootstrap methods into. + */ + private void copyBootstrapMethods(final ClassWriter classWriter, + final Item[] items, final char[] c) { + // finds the "BootstrapMethods" attribute + int u = getAttributes(); + boolean found = false; + for (int i = readUnsignedShort(u); i > 0; --i) { + String attrName = readUTF8(u + 2, c); + if ("BootstrapMethods".equals(attrName)) { + found = true; + break; + } + u += 6 + readInt(u + 4); + } + if (!found) { + return; + } + // copies the bootstrap methods in the class writer + int boostrapMethodCount = readUnsignedShort(u + 8); + for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) { + int position = v - u - 10; + int hashCode = readConst(readUnsignedShort(v), c).hashCode(); + for (int k = readUnsignedShort(v + 2); k > 0; --k) { + hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode(); + v += 2; + } + v += 4; + Item item = new Item(j); + item.set(position, hashCode & 0x7FFFFFFF); + int index = item.hashCode % items.length; + item.next = items[index]; + items[index] = item; + } + int attrSize = readInt(u + 4); + ByteVector bootstrapMethods = new ByteVector(attrSize + 62); + bootstrapMethods.putByteArray(b, u + 10, attrSize - 2); + classWriter.bootstrapMethodsCount = boostrapMethodCount; + classWriter.bootstrapMethods = bootstrapMethods; + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param is an input stream from which to read the class. + * @throws IOException if a problem occurs during reading. + */ + public ClassReader(final InputStream is) throws IOException { + this(readClass(is)); + } + + /** + * Constructs a new {@link ClassReader} object. + * + * @param name the fully qualified name of the class to be read. + * @throws IOException if an exception occurs during reading. + */ + public ClassReader(final String name) throws IOException { + this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/') + + ".class")); + } + + /** + * Reads the bytecode of a class. + * + * @param is an input stream from which to read the class. + * @return the bytecode read from the given input stream. + * @throws IOException if a problem occurs during reading. + */ + private static byte[] readClass(final InputStream is) throws IOException { + if (is == null) { + throw new IOException("Class not found"); + } + byte[] b = new byte[is.available()]; + int len = 0; + while (true) { + int n = is.read(b, len, b.length - len); + if (n == -1) { + if (len < b.length) { + byte[] c = new byte[len]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + return b; + } + len += n; + if (len == b.length) { + int last = is.read(); + if (last < 0) { + return b; + } + byte[] c = new byte[b.length + 1000]; + System.arraycopy(b, 0, c, 0, len); + c[len++] = (byte) last; + b = c; + } + } + } + + // ------------------------------------------------------------------------ + // Public methods + // ------------------------------------------------------------------------ + + /** + * Makes the given visitor visit the Java class of this {@link ClassReader}. + * This class is the one specified in the constructor (see + * {@link #ClassReader(byte[]) ClassReader}). + * + * @param classVisitor the visitor that must visit this class. + * @param skipDebug <tt>true</tt> if the debug information of the class + * must not be visited. In this case the + * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and + * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will + * not be called. + */ + public void accept(final ClassVisitor classVisitor, final boolean skipDebug) + { + accept(classVisitor, new Attribute[0], skipDebug); + } + + /** + * Makes the given visitor visit the Java class of this {@link ClassReader}. + * This class is the one specified in the constructor (see + * {@link #ClassReader(byte[]) ClassReader}). + * + * @param classVisitor the visitor that must visit this class. + * @param attrs prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to the + * type of one the prototypes will be ignored. + * @param skipDebug <tt>true</tt> if the debug information of the class + * must not be visited. In this case the + * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and + * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will + * not be called. + */ + public void accept( + final ClassVisitor classVisitor, + final Attribute[] attrs, + final boolean skipDebug) + { + byte[] b = this.b; // the bytecode array + char[] c = new char[maxStringLength]; // buffer used to read strings + int i, j, k; // loop variables + int u, v, w; // indexes in b + Attribute attr; + + int access; + String name; + String desc; + String attrName; + String signature; + int anns = 0; + int ianns = 0; + int xanns = 0; + int ixanns = 0; + Attribute cattrs = null; + + // visits the header + u = header; // u = u2 access_flags + access = readUnsignedShort(u); + name = readClass(u + 2, c); // name = u2 this_class + v = items[readUnsignedShort(u + 4)]; // u + 4 = u2 super_class + String superClassName = v == 0 ? null : readUTF8(v, c); + String[] implementedItfs = new String[readUnsignedShort(u + 6)]; + // u + 6 = u2 interfaces_count; + w = 0; + u += 8; // u + 8 = interfaces[interfaces_count] + for (i = 0; i < implementedItfs.length; ++i) { + implementedItfs[i] = readClass(u, c); + u += 2; + } + + // u = u2 fields_count + + // skips fields and methods + v = u; + i = readUnsignedShort(v); // i = u2 fields_count + v += 2; // v = field_info fields[fields_count] + for (; i > 0; --i) { + j = readUnsignedShort(v + 6); + v += 8; + for (; j > 0; --j) { + v += 6 + readInt(v + 2); + } + } + i = readUnsignedShort(v); // i = u2 methods_count + v += 2; // v = method_info methods[methods_count] + for (; i > 0; --i) { + j = readUnsignedShort(v + 6); + v += 8; + for (; j > 0; --j) { + v += 6 + readInt(v + 2); + } + } + // reads the class's attributes + signature = null; + String sourceFile = null; + String sourceDebug = null; + String enclosingOwner = null; + String enclosingName = null; + String enclosingDesc = null; + + i = readUnsignedShort(v); // i = u2 attributes_count + v += 2; // v = attribute_info attributes[attributes_count] + for (; i > 0; --i) { + attrName = readUTF8(v, c); + if (attrName.equals("SourceFile")) { + sourceFile = readUTF8(v + 6, c); + } else if (attrName.equals("Deprecated")) { + access |= Opcodes.ACC_DEPRECATED; + } else if (attrName.equals("Synthetic")) { + access |= Opcodes.ACC_SYNTHETIC; + } else if (attrName.equals("Annotation")) { + access |= Opcodes.ACC_ANNOTATION; + } else if (attrName.equals("Enum")) { + access |= Opcodes.ACC_ENUM; + } else if (attrName.equals("InnerClasses")) { + w = v + 6; + } else if (attrName.equals("Signature")) { + signature = readUTF8(v + 6, c); + } else if (attrName.equals("SourceDebugExtension")) { + int len = readInt(v + 2); + sourceDebug = readUTF(v + 6, len, new char[len]); + } else if (attrName.equals("EnclosingMethod")) { + enclosingOwner = readClass(v + 6, c); + int item = readUnsignedShort(v + 8); + if (item != 0) { + enclosingName = readUTF8(items[item], c); + enclosingDesc = readUTF8(items[item] + 2, c); + } + } else if (attrName.equals("RuntimeVisibleAnnotations")) { + anns = v + 6; + } else if (attrName.equals("RuntimeInvisibleAnnotations")) { + ianns = v + 6; + } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) { + xanns = v + 6; + } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) { + ixanns = v + 6; + } else if (attrName.equals("BootstrapMethods")) { + bootstrapMethods = new int[readUnsignedShort(v + 6)]; + for (j = 0, u = v + 8; j < bootstrapMethods.length; j++) { + bootstrapMethods[j] = u; + u += 2 + readUnsignedShort(u + 2) << 1; + } + } else { + attr = readAttribute(attrs, + attrName, + v + 6, + readInt(v + 2), + c, + -1, + null); + if (attr != null) { + attr.next = cattrs; + cattrs = attr; + } + } + v += 6 + readInt(v + 2); + } + // calls the visit method + classVisitor.visit(readInt(4), + access, + name, + signature, + superClassName, + implementedItfs); + + // calls the visitSource method + if (sourceFile != null || sourceDebug != null) { + classVisitor.visitSource(sourceFile, sourceDebug); + } + + // calls the visitOuterClass method + if (enclosingOwner != null) { + classVisitor.visitOuterClass(enclosingOwner, + enclosingName, + enclosingDesc); + } + + // visits the class annotations + for (i = 1; i >= 0; --i) { + v = i == 0 ? ianns : anns; + if (v != 0) { + j = readUnsignedShort(v); + v += 2; + for (; j > 0; --j) { + desc = readUTF8(v, c); + v += 2; + v = readAnnotationValues(v, + c, + classVisitor.visitAnnotation(desc, i != 0)); + } + } + } + + // TODO + // visits the class extended annotations + for (i = 1; i >= 0; --i) { + v = i == 0 ? ixanns : xanns; + if (v != 0) { + j = readUnsignedShort(v); + v += 2; + for (; j > 0; --j) { + v = readTypeAnnotationValues(v, + c, classVisitor, i != 0); + } + } + } + + // visits the class attributes + while (cattrs != null) { + attr = cattrs.next; + cattrs.next = null; + classVisitor.visitAttribute(cattrs); + cattrs = attr; + } + + // class the visitInnerClass method + if (w != 0) { + i = readUnsignedShort(w); + w += 2; + for (; i > 0; --i) { + classVisitor.visitInnerClass(readUnsignedShort(w) == 0 + ? null + : readClass(w, c), readUnsignedShort(w + 2) == 0 + ? null + : readClass(w + 2, c), readUnsignedShort(w + 4) == 0 + ? null + : readUTF8(w + 4, c), readUnsignedShort(w + 6)); + w += 8; + } + } + + // visits the fields + u = header + 8 + 2 * implementedItfs.length; + i = readUnsignedShort(u); // i = u2 fields_count + u += 2; // u = field_info[fields_count] + for (; i > 0; --i) { + access = readUnsignedShort(u); + name = readUTF8(u + 2, c); + desc = readUTF8(u + 4, c); + // visits the field's attributes and looks for a ConstantValue + // attribute + int fieldValueItem = 0; + signature = null; + anns = 0; + ianns = 0; + xanns = 0; + ixanns = 0; + cattrs = null; + + j = readUnsignedShort(u + 6); // j = u2 attributes_count + u += 8; // u = attributes[attributes_count] + for (; j > 0; --j) { + attrName = readUTF8(u, c); + if (attrName.equals("ConstantValue")) { + fieldValueItem = readUnsignedShort(u + 6); + } else if (attrName.equals("Synthetic")) { + access |= Opcodes.ACC_SYNTHETIC; + } else if (attrName.equals("Deprecated")) { + access |= Opcodes.ACC_DEPRECATED; + } else if (attrName.equals("Enum")) { + access |= Opcodes.ACC_ENUM; + } else if (attrName.equals("Signature")) { + signature = readUTF8(u + 6, c); + } else if (attrName.equals("RuntimeVisibleAnnotations")) { + anns = u + 6; + } else if (attrName.equals("RuntimeInvisibleAnnotations")) { + ianns = u + 6; + } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) { + xanns = u + 6; + } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) { + ixanns = u + 6; + } else { + + attr = readAttribute(attrs, + attrName, + u + 6, + readInt(u + 2), + c, + -1, + null); + if (attr != null) { + attr.next = cattrs; + cattrs = attr; + } + } + u += 6 + readInt(u + 2); + } + // reads the field's value, if any + Object value = (fieldValueItem == 0 + ? null + : readConst(fieldValueItem, c)); + // visits the field + FieldVisitor fv = classVisitor.visitField(access, + name, + desc, + signature, + value); + // visits the field annotations and attributes + if (fv != null) { + for (j = 1; j >= 0; --j) { + v = j == 0 ? ianns : anns; + if (v != 0) { + k = readUnsignedShort(v); + v += 2; + for (; k > 0; --k) { + desc = readUTF8(v, c); + v += 2; + v = readAnnotationValues(v, + c, + fv.visitAnnotation(desc, j != 0)); + } + } + } + //TODO + // now visit the extended annotations + if(xanns != 0) { + v = xanns; + k = readUnsignedShort(v); + v += 2; + for(; k > 0; --k) { + v = readTypeAnnotationValues(v, + c, fv, true); + } + } + + if(ixanns != 0) { + v = ixanns; + k = readUnsignedShort(v); + v += 2; + for(; k > 0; --k) { + v = readTypeAnnotationValues(v, + c, fv, false); + } + } + + while (cattrs != null) { + attr = cattrs.next; + cattrs.next = null; + fv.visitAttribute(cattrs); + cattrs = attr; + } + fv.visitEnd(); + } + } + + // visits the methods + i = readUnsignedShort(u); + u += 2; + for (; i > 0; --i) { + int u0 = u + 6; + access = readUnsignedShort(u); + name = readUTF8(u + 2, c); + desc = readUTF8(u + 4, c); + signature = null; + anns = 0; + ianns = 0; + //jaime + xanns = 0; + ixanns = 0; + // end jaime + int dann = 0; + int mpanns = 0; + int impanns = 0; + cattrs = null; + v = 0; + w = 0; + + // looks for Code and Exceptions attributes + j = readUnsignedShort(u + 6); + u += 8; + for (; j > 0; --j) { + attrName = readUTF8(u, c); + u += 2; + int attrSize = readInt(u); + u += 4; + if (attrName.equals("Code")) { + v = u; + } else if (attrName.equals("Exceptions")) { + w = u; + } else if (attrName.equals("Synthetic")) { + access |= Opcodes.ACC_SYNTHETIC; + } else if (attrName.equals("Varargs")) { + access |= Opcodes.ACC_VARARGS; + } else if (attrName.equals("Bridge")) { + access |= Opcodes.ACC_BRIDGE; + } else if (attrName.equals("Deprecated")) { + access |= Opcodes.ACC_DEPRECATED; + } else if (attrName.equals("Signature")) { + signature = readUTF8(u, c); + } else if (attrName.equals("AnnotationDefault")) { + dann = u; + } else if (attrName.equals("RuntimeVisibleAnnotations")) { + anns = u; + } else if (attrName.equals("RuntimeInvisibleAnnotations")) { + ianns = u; + } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) { + xanns = u; + } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) { + ixanns = u; + } else if (attrName.equals("RuntimeVisibleParameterAnnotations")) { + mpanns = u; + } else if (attrName.equals("RuntimeInvisibleParameterAnnotations")) { + impanns = u; + } else { + attr = readAttribute(attrs, + attrName, + u, + attrSize, + c, + -1, + null); + if (attr != null) { + attr.next = cattrs; + cattrs = attr; + } + } + u += attrSize; + } + // reads declared exceptions + String[] exceptions; + if (w == 0) { + exceptions = null; + } else { + exceptions = new String[readUnsignedShort(w)]; + w += 2; + for (j = 0; j < exceptions.length; ++j) { + exceptions[j] = readClass(w, c); + w += 2; + } + } + + // visits the method's code, if any + MethodVisitor mv = classVisitor.visitMethod(access, + name, + desc, + signature, + exceptions); + + if (mv != null) { + /* + * if the returned MethodVisitor is in fact a MethodWriter, it + * means there is no method adapter between the reader and the + * writer. If, in addition, the writer's constant pool was + * copied from this reader (mw.cw.cr == this), and the signature + * and exceptions of the method have not been changed, then it + * is possible to skip all visit events and just copy the + * original code of the method to the writer (the access, name + * and descriptor can have been changed, this is not important + * since they are not copied as is from the reader). + */ + if (mv instanceof MethodWriter) { + MethodWriter mw = (MethodWriter) mv; + if (mw.cw.cr == this) { + if (signature == mw.signature) { + boolean sameExceptions = false; + if (exceptions == null) { + sameExceptions = mw.exceptionCount == 0; + } else { + if (exceptions.length == mw.exceptionCount) { + sameExceptions = true; + for (j = exceptions.length - 1; j >= 0; --j) + { + w -= 2; + if (mw.exceptions[j] != readUnsignedShort(w)) + { + sameExceptions = false; + break; + } + } + } + } + if (sameExceptions) { + /* + * we do not copy directly the code into + * MethodWriter to save a byte array copy + * operation. The real copy will be done in + * ClassWriter.toByteArray(). + */ + mw.classReaderOffset = u0; + mw.classReaderLength = u - u0; + continue; + } + } + } + } + if (dann != 0) { + AnnotationVisitor dv = mv.visitAnnotationDefault(); + readAnnotationValue(dann, c, null, dv); + dv.visitEnd(); + } + for (j = 1; j >= 0; --j) { + w = j == 0 ? ianns : anns; + if (w != 0) { + k = readUnsignedShort(w); + w += 2; + for (; k > 0; --k) { + desc = readUTF8(w, c); + w += 2; + w = readAnnotationValues(w, + c, + mv.visitAnnotation(desc, j != 0)); + } + } + } + + // now visit the method extended annotations + for (j = 1; j >= 0; --j) { + w = j == 0 ? ixanns : xanns; + if (w != 0) { + k = readUnsignedShort(w); + w += 2; + for (; k > 0; --k) { + w = readTypeAnnotationValues(w, + c, mv, j != 0); + } + } + } + + if (mpanns != 0) { + readParameterAnnotations(mpanns, c, true, mv); + } + if (impanns != 0) { + readParameterAnnotations(impanns, c, false, mv); + } + + while (cattrs != null) { + attr = cattrs.next; + cattrs.next = null; + mv.visitAttribute(cattrs); + cattrs = attr; + } + } + + if (mv != null && v != 0) { + int maxStack = readUnsignedShort(v); + int maxLocals = readUnsignedShort(v + 2); + int codeLength = readInt(v + 4); + v += 8; + + int codeStart = v; + int codeEnd = v + codeLength; + + mv.visitCode(); + + // 1st phase: finds the labels + int label; + Label[] labels = new Label[codeLength + 1]; + while (v < codeEnd) { + int opcode = b[v] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + v += 1; + break; + case ClassWriter.LABEL_INSN: + label = v - codeStart + readShort(v + 1); + if (labels[label] == null) { + labels[label] = new Label(); + } + v += 3; + break; + case ClassWriter.LABELW_INSN: + label = v - codeStart + readInt(v + 1); + if (labels[label] == null) { + labels[label] = new Label(); + } + v += 5; + break; + case ClassWriter.WIDE_INSN: + opcode = b[v + 1] & 0xFF; + if (opcode == Opcodes.IINC) { + v += 6; + } else { + v += 4; + } + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + w = v - codeStart; + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + j = readInt(v); + v += 4; + j = readInt(v) - j + 1; + v += 4; + for (; j > 0; --j) { + label = w + readInt(v); + v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + } + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + w = v - codeStart; + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + j = readInt(v); + v += 4; + for (; j > 0; --j) { + v += 4; // skips key + label = w + readInt(v); + v += 4; + if (labels[label] == null) { + labels[label] = new Label(); + } + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + v += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + v += 3; + break; + case ClassWriter.ITFMETH_INSN: + case ClassWriter.INDY: + v += 5; + break; + // case MANA_INSN: + default: + v += 4; + break; + } + } + // parses the try catch entries + j = readUnsignedShort(v); + v += 2; + for (; j > 0; --j) { + label = readUnsignedShort(v); + Label start = labels[label]; + if (start == null) { + labels[label] = start = new Label(); + } + label = readUnsignedShort(v + 2); + Label end = labels[label]; + if (end == null) { + labels[label] = end = new Label(); + } + label = readUnsignedShort(v + 4); + Label handler = labels[label]; + if (handler == null) { + labels[label] = handler = new Label(); + } + + int type = readUnsignedShort(v + 6); + if (type == 0) { + mv.visitTryCatchBlock(start, end, handler, null); + } else { + mv.visitTryCatchBlock(start, + end, + handler, + readUTF8(items[type], c)); + } + v += 8; + } + // parses the local variable, line number tables, and code + // attributes + int varTable = 0; + int varTypeTable = 0; + cattrs = null; + j = readUnsignedShort(v); + v += 2; + for (; j > 0; --j) { + attrName = readUTF8(v, c); + if (attrName.equals("LocalVariableTable")) { + if (!skipDebug) { + varTable = v + 6; + k = readUnsignedShort(v + 6); + w = v + 8; + for (; k > 0; --k) { + label = readUnsignedShort(w); + if (labels[label] == null) { + labels[label] = new Label(); + } + label += readUnsignedShort(w + 2); + if (labels[label] == null) { + labels[label] = new Label(); + } + w += 10; + } + } + } else if (attrName.equals("LocalVariableTypeTable")) { + varTypeTable = v + 6; + } else if (attrName.equals("LineNumberTable")) { + if (!skipDebug) { + k = readUnsignedShort(v + 6); + w = v + 8; + for (; k > 0; --k) { + label = readUnsignedShort(w); + if (labels[label] == null) { + labels[label] = new Label(); + } + labels[label].line = readUnsignedShort(w + 2); + w += 4; + } + } + } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) { + k = readUnsignedShort(v + 6); + w = v + 8; + for (; k > 0; --k) { + w = readTypeAnnotationValues(w, + c, mv, false); + } + } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) { + k = readUnsignedShort(v + 6); + w = v + 8; + for (; k > 0; --k) { + w = readTypeAnnotationValues(w, + c, mv, true); + } + } else { + for (k = 0; k < attrs.length; ++k) { + if (attrs[k].type.equals(attrName)) { + attr = attrs[k].read(this, + v + 6, + readInt(v + 2), + c, + codeStart - 8, + labels); + if (attr != null) { + attr.next = cattrs; + cattrs = attr; + } + } + } + } + v += 6 + readInt(v + 2); + } + + // 2nd phase: visits each instruction + v = codeStart; + PrecompiledMethodVisitor pmv = + (mv instanceof PrecompiledMethodVisitor) + ? (PrecompiledMethodVisitor) mv : null; + Label l; + while (v < codeEnd) { + w = v - codeStart; + if (pmv != null) + pmv.visitCurrentPosition(w); + l = labels[w]; + if (l != null) { + mv.visitLabel(l); + if (!skipDebug && l.line > 0) { + mv.visitLineNumber(l.line, l); + } + } + int opcode = b[v] & 0xFF; + switch (ClassWriter.TYPE[opcode]) { + case ClassWriter.NOARG_INSN: + mv.visitInsn(opcode); + v += 1; + break; + case ClassWriter.IMPLVAR_INSN: + if (opcode > Opcodes.ISTORE) { + opcode -= 59; // ISTORE_0 + mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), + opcode & 0x3); + } else { + opcode -= 26; // ILOAD_0 + mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), + opcode & 0x3); + } + v += 1; + break; + case ClassWriter.LABEL_INSN: + mv.visitJumpInsn(opcode, labels[w + + readShort(v + 1)]); + v += 3; + break; + case ClassWriter.LABELW_INSN: + mv.visitJumpInsn(opcode - 33, labels[w + + readInt(v + 1)]); + v += 5; + break; + case ClassWriter.WIDE_INSN: + opcode = b[v + 1] & 0xFF; + if (opcode == Opcodes.IINC) { + mv.visitIincInsn(readUnsignedShort(v + 2), + readShort(v + 4)); + v += 6; + } else { + mv.visitVarInsn(opcode, + readUnsignedShort(v + 2)); + v += 4; + } + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + v += 4; + int min = readInt(v); + v += 4; + int max = readInt(v); + v += 4; + Label[] table = new Label[max - min + 1]; + for (j = 0; j < table.length; ++j) { + table[j] = labels[w + readInt(v)]; + v += 4; + } + mv.visitTableSwitchInsn(min, + max, + labels[label], + table); + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + v += 4; + j = readInt(v); + v += 4; + int[] keys = new int[j]; + Label[] values = new Label[j]; + for (j = 0; j < keys.length; ++j) { + keys[j] = readInt(v); + v += 4; + values[j] = labels[w + readInt(v)]; + v += 4; + } + mv.visitLookupSwitchInsn(labels[label], + keys, + values); + break; + case ClassWriter.VAR_INSN: + mv.visitVarInsn(opcode, b[v + 1] & 0xFF); + v += 2; + break; + case ClassWriter.SBYTE_INSN: + mv.visitIntInsn(opcode, b[v + 1]); + v += 2; + break; + case ClassWriter.SHORT_INSN: + mv.visitIntInsn(opcode, readShort(v + 1)); + v += 3; + break; + case ClassWriter.LDC_INSN: + mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); + v += 2; + break; + case ClassWriter.LDCW_INSN: + mv.visitLdcInsn(readConst(readUnsignedShort(v + 1), + c)); + v += 3; + break; + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.ITFMETH_INSN: + int cpIndex = items[readUnsignedShort(v + 1)]; + String iowner = readClass(cpIndex, c); + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + String iname = readUTF8(cpIndex, c); + String idesc = readUTF8(cpIndex + 2, c); + if (opcode < Opcodes.INVOKEVIRTUAL) { + mv.visitFieldInsn(opcode, iowner, iname, idesc); + } else { + mv.visitMethodInsn(opcode, iowner, iname, idesc); + } + if (opcode == Opcodes.INVOKEINTERFACE) { + v += 5; + } else { + v += 3; + } + break; + case ClassWriter.TYPE_INSN: + mv.visitTypeInsn(opcode, readClass(v + 1, c)); + v += 3; + break; + case ClassWriter.IINC_INSN: + mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); + v += 3; + break; + case ClassWriter.INDY: + cpIndex = items[readUnsignedShort(v + 1)]; + int bsmIndex = bootstrapMethods[readUnsignedShort(cpIndex)]; + Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c); + int bsmArgCount = readUnsignedShort(bsmIndex + 2); + Object[] bsmArgs = new Object[bsmArgCount]; + bsmIndex += 4; + for (j = 0; j < bsmArgCount; j++) { + bsmArgs[j] = readConst(readUnsignedShort(bsmIndex), c); + bsmIndex += 2; + } + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + iname = readUTF8(cpIndex, c); + idesc = readUTF8(cpIndex + 2, c); + mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs); + v += 5; + break; + // case MANA_INSN: + default: + mv.visitMultiANewArrayInsn(readClass(v + 1, c), + b[v + 3] & 0xFF); + v += 4; + break; + } + } + l = labels[codeEnd - codeStart]; + if (l != null) { + if (pmv != null) + pmv.visitCurrentPosition(codeEnd - codeStart); + mv.visitLabel(l); + } + + // visits the local variable tables + if (!skipDebug && varTable != 0) { + int[] typeTable = null; + if (varTypeTable != 0) { + w = varTypeTable; + k = readUnsignedShort(w) * 3; + w += 2; + typeTable = new int[k]; + while (k > 0) { + typeTable[--k] = w + 6; // signature + typeTable[--k] = readUnsignedShort(w + 8); // index + typeTable[--k] = readUnsignedShort(w); // start + w += 10; + } + } + w = varTable; + k = readUnsignedShort(w); + w += 2; + for (; k > 0; --k) { + int start = readUnsignedShort(w); + int length = readUnsignedShort(w + 2); + int index = readUnsignedShort(w + 8); + String vsignature = null; + if (typeTable != null) { + for (int a = 0; a < typeTable.length; a += 3) { + if (typeTable[a] == start + && typeTable[a + 1] == index) + { + vsignature = readUTF8(typeTable[a + 2], c); + break; + } + } + } + mv.visitLocalVariable(readUTF8(w + 4, c), + readUTF8(w + 6, c), + vsignature, + labels[start], + labels[start + length], + index); + w += 10; + } + } + // visits the other attributes + while (cattrs != null) { + attr = cattrs.next; + cattrs.next = null; + mv.visitAttribute(cattrs); + cattrs = attr; + } + // visits the max stack and max locals values + mv.visitMaxs(maxStack, maxLocals); + } + + if (mv != null) { + mv.visitEnd(); + } + } + + // visits the end of the class + classVisitor.visitEnd(); + } + + /** + * Reads parameter annotations and makes the given visitor visit them. + * + * @param v start offset in {@link #b b} of the annotations to be read. + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param visible <tt>true</tt> 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 char[] buf, + final boolean visible, + final MethodVisitor mv) + { + int n = b[v++] & 0xFF; + for (int i = 0; i < n; ++i) { + int j = readUnsignedShort(v); + v += 2; + for (; j > 0; --j) { + String desc = readUTF8(v, buf); + v += 2; + AnnotationVisitor av = mv.visitParameterAnnotation(i, + desc, + visible); + v = readAnnotationValues(v, buf, av); + } + } + } + + /** + * Reads the values of an annotation and makes the given visitor visit them. + * + * @param v the start offset in {@link #b b} of the values to be read + * (including the unsigned short that gives the number of values). + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param av the visitor that must visit the values. + * @return the end offset of the annotations values. + */ + private int readAnnotationValues( + int v, + final char[] buf, + final AnnotationVisitor av) + { + int i = readUnsignedShort(v); + v += 2; + for (; i > 0; --i) { + String name = readUTF8(v, buf); + v += 2; + v = readAnnotationValue(v, buf, name, av); + } + av.visitEnd(); + return v; + } + /** + * Reads the values and reference info of an extended annotation + * and makes the given visitor visit them. + * + * @param v the start offset in {@link #b b} of the values to be read + * (including the unsigned short that gives the number of values). + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param mv the visitor to generate the visitor that must visit the values. + * @param visible {@code true} if the annotation is visible at runtime. + * @return the end offset of the annotations values. + * @author jaimeq + */ + private int readTypeAnnotationValues( + int v, + final char[] buf, + final MemberVisitor mv, + final boolean visible) + { + // first handle + // + // u1 target_type + // { ... + // } reference_info + // + + int target_type_value = readByte(v); + v += 1; + + Integer offset = null; + Integer location_length = null; + List<TypePathEntry> locations = new ArrayList<TypePathEntry>(); + Integer start_pc = null; + Integer length = null; + Integer index = null; + Integer param_index = null; + Integer bound_index = null; + Integer type_index = null; + Integer exception_index = null; + Integer table_length = null; + + TargetType target_type = TargetType.fromTargetTypeValue(target_type_value); + + switch(target_type) { + // type test (instanceof) + // object creation + // constructor/method reference receiver + // { + // u2 offset; + // } reference_info; + case INSTANCEOF: + case NEW: + case CONSTRUCTOR_REFERENCE: + case METHOD_REFERENCE: + offset = readUnsignedShort(v); + v += 2; + break; + + // method receiver + // { + // } reference_info; + case METHOD_RECEIVER: + break; + + // local variable + // u2 table_length; + // { + // u2 start_pc; + // u2 length; + // u2 index; + // } reference_info; + case LOCAL_VARIABLE: + // resource variable + case RESOURCE_VARIABLE: + table_length = readUnsignedShort(v); + v += 2; + assert table_length == 1; // FIXME + start_pc = readUnsignedShort(v); + v += 2; + length = readUnsignedShort(v); + v += 2; + index = readUnsignedShort(v); + v += 2; + break; + + // method return type + // { + // } reference_info; + case METHOD_RETURN: + break; + + // method parameter + // { + // u1 param; + // } reference_info; + case METHOD_FORMAL_PARAMETER: + param_index = readByte(v); + v++; + break; + + // field + // { + // } reference_info; + case FIELD: + break; + + // class type parameter bound + // method type parameter bound + // { + // u1 param_index; + // u1 bound_index; + // } reference_info; + case CLASS_TYPE_PARAMETER_BOUND: + case METHOD_TYPE_PARAMETER_BOUND: + param_index = readByte(v); + v++; + bound_index = readByte(v); + v++; + break; + + // class extends/implements + // exception type in throws + // { + // u1 type_index; + // } reference_info; + case CLASS_EXTENDS: + type_index = readUnsignedShort(v); + if (type_index == 0xFFFF) type_index = -1; + v += 2; + break; + case THROWS: + type_index = readUnsignedShort(v); + v += 2; + break; + case EXCEPTION_PARAMETER: + exception_index = readUnsignedShort(v); + v += 2; + break; + + // typecast + // type argument in constructor call + // type argument in method call + // type argument in constructor reference + // type argument in method reference + // { + // u2 offset; + // u1 type_index; + // } reference_info; + case CAST: + case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case METHOD_INVOCATION_TYPE_ARGUMENT: + case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case METHOD_REFERENCE_TYPE_ARGUMENT: + offset = readUnsignedShort(v); + v += 2; + + type_index = readByte(v); + v++; + break; + + // method type parameter + // { + // u1 param_index; + // } reference_info; + case CLASS_TYPE_PARAMETER: + case METHOD_TYPE_PARAMETER: + param_index = readByte(v); + v++; + break; + + default: throw new RuntimeException( + "Unrecognized target type: " + target_type); + } + + // now read in the location information + { + location_length = readByte(v); + v += 1; + for (int m = 0; m < location_length; m++) { + int loctag = readByte(v); + int locarg = readByte(v + 1); + v += TypePathEntry.bytesPerEntry; + locations.add(TypePathEntry.fromBinary(loctag, locarg)); + } + } + + String desc = readUTF8(v, buf); + v += 2; + TypeAnnotationVisitor xav = mv.visitTypeAnnotation(desc, visible, false); + + xav.visitXTargetType(target_type_value); + if (start_pc != null) { + xav.visitXStartPc(start_pc); + } + if (length != null) { + xav.visitXLength(length); + } + if (index != null) { + xav.visitXIndex(index); + } + if (offset != null) { + xav.visitXOffset(offset); + } + if (type_index != null) { + xav.visitXTypeIndex(type_index); + } + if (param_index != null) { + xav.visitXParamIndex(param_index); + } + if (bound_index != null) { + xav.visitXBoundIndex(bound_index); + } + if (exception_index != null) { + xav.visitXExceptionIndex(exception_index); + } + if (location_length != null) { + xav.visitXLocationLength(location_length); + } + for (TypePathEntry location : locations) { + xav.visitXLocation(location); + } + // Visit the annotation name and save space for the values count. + xav.visitXNameAndArgsSize(); + + // then read annotation values + int i = readUnsignedShort(v); + v += 2; + for (; i > 0; --i) { + String name = readUTF8(v, buf); + v += 2; + // can use the same method as for declaration annotations because + // the first part of an extended annotation matches the normal + // annotations + v = readAnnotationValue(v, buf, name, xav); + } + + xav.visitEnd(); + return v; + } + + /** + * Reads a value of an annotation and makes the given visitor visit it. + * + * @param v the start offset in {@link #b b} of the value to be read (<i>not + * including the value name constant pool index</i>). + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param name the name of the value to be read. + * @param av the visitor that must visit the value. + * @return the end offset of the annotation value. + */ + private int readAnnotationValue( + int v, + final char[] buf, + final String name, + final AnnotationVisitor av) + { + int i; + switch (readByte(v++)) { + case 'I': // pointer to CONSTANT_Integer + case 'J': // pointer to CONSTANT_Long + case 'F': // pointer to CONSTANT_Float + case 'D': // pointer to CONSTANT_Double + av.visit(name, readConst(readUnsignedShort(v), buf)); + v += 2; + break; + case 'B': // pointer to CONSTANT_Byte + av.visit(name, + new Byte((byte) readInt(items[readUnsignedShort(v)]))); + v += 2; + break; + case 'Z': // pointer to CONSTANT_Boolean + boolean b = readInt(items[readUnsignedShort(v)]) == 0; + av.visit(name, b ? Boolean.FALSE : Boolean.TRUE); + v += 2; + break; + case 'S': // pointer to CONSTANT_Short + av.visit(name, + new Short((short) readInt(items[readUnsignedShort(v)]))); + v += 2; + break; + case 'C': // pointer to CONSTANT_Char + av.visit(name, + new Character((char) readInt(items[readUnsignedShort(v)]))); + v += 2; + break; + case 's': // pointer to CONSTANT_Utf8 + av.visit(name, readUTF8(v, buf)); + v += 2; + break; + case 'e': // enum_const_value + av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); + v += 4; + break; + case 'c': // class_info + av.visit(name, Type.getType(readUTF8(v, buf))); + v += 2; + break; + case '@': // annotation_value + String desc = readUTF8(v, buf); + v += 2; + v = readAnnotationValues(v, buf, av.visitAnnotation(name, desc)); + break; + case '[': // array_value + int size = readUnsignedShort(v); + v += 2; + if (size == 0) { + av.visitArray(name).visitEnd(); + return v; + } + switch (readByte(v++)) { + case 'B': + byte[] bv = new byte[size]; + for (i = 0; i < size; i++) { + bv[i] = (byte) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, bv); + --v; + break; + case 'Z': + boolean[] zv = new boolean[size]; + for (i = 0; i < size; i++) { + zv[i] = readInt(items[readUnsignedShort(v)]) != 0; + v += 3; + } + av.visit(name, zv); + --v; + break; + case 'S': + short[] sv = new short[size]; + for (i = 0; i < size; i++) { + sv[i] = (short) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, sv); + --v; + break; + case 'C': + char[] cv = new char[size]; + for (i = 0; i < size; i++) { + cv[i] = (char) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, cv); + --v; + break; + case 'I': + int[] iv = new int[size]; + for (i = 0; i < size; i++) { + iv[i] = readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, iv); + --v; + break; + case 'J': + long[] lv = new long[size]; + for (i = 0; i < size; i++) { + lv[i] = readLong(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, lv); + --v; + break; + case 'F': + float[] fv = new float[size]; + for (i = 0; i < size; i++) { + fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, fv); + --v; + break; + case 'D': + double[] dv = new double[size]; + for (i = 0; i < size; i++) { + dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, dv); + --v; + break; + default: + v--; + AnnotationVisitor aav = av.visitArray(name); + for (i = size; i > 0; --i) { + v = readAnnotationValue(v, buf, null, aav); + } + aav.visitEnd(); + } + } + return v; + } + + /** + * Returns the start index of the attribute_info structure of this class. + * + * @return the start index of the attribute_info structure of this class. + */ + private int getAttributes() { + // skips the header + int u = header + 8 + readUnsignedShort(header + 6) * 2; + // skips fields and methods + for (int i = readUnsignedShort(u); i > 0; --i) { + for (int j = readUnsignedShort(u + 8); j > 0; --j) { + u += 6 + readInt(u + 12); + } + u += 8; + } + u += 2; + for (int i = readUnsignedShort(u); i > 0; --i) { + for (int j = readUnsignedShort(u + 8); j > 0; --j) { + u += 6 + readInt(u + 12); + } + u += 8; + } + // the attribute_info structure starts just after the methods + return u + 2; + } + + /** + * Reads an attribute in {@link #b b}. + * + * @param attrs prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to the + * type of one the prototypes is ignored (i.e. an empty + * {@link Attribute} instance is returned). + * @param type the type of the attribute. + * @param off index of the first byte of the attribute's content in + * {@link #b b}. The 6 attribute header bytes, containing the type + * and the length of the attribute, are not taken into account here + * (they have already been read). + * @param len the length of the attribute's content. + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param codeOff index of the first byte of code's attribute content in + * {@link #b b}, or -1 if the attribute to be read is not a code + * attribute. The 6 attribute header bytes, containing the type and + * the length of the attribute, are not taken into account here. + * @param labels the labels of the method's code, or <tt>null</tt> if the + * attribute to be read is not a code attribute. + * @return the attribute that has been read, or <tt>null</tt> to skip this + * attribute. + */ + private Attribute readAttribute( + final Attribute[] attrs, + final String type, + final int off, + final int len, + final char[] buf, + final int codeOff, + final Label[] labels) + { + for (int i = 0; i < attrs.length; ++i) { + if (attrs[i].type.equals(type)) { + return attrs[i].read(this, off, len, buf, codeOff, labels); + } + } + return new Attribute(type).read(this, off, len, null, -1, null); + } + + // ------------------------------------------------------------------------ + // Utility methods: low level parsing + // ------------------------------------------------------------------------ + + /** + * Returns the start index of the constant pool item in {@link #b b}, plus + * one. <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param item the index a constant pool item. + * @return the start index of the constant pool item in {@link #b b}, plus + * one. + */ + public int getItem(final int item) { + return items[item]; + } + + /** + * Reads a byte value in {@link #b b}. <i>This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters.</i> + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readByte(final int index) { + return b[index] & 0xFF; + } + + /** + * Reads an unsigned short value in {@link #b b}. <i>This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters.</i> + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readUnsignedShort(final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); + } + + /** + * Reads a signed short value in {@link #b b}. <i>This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters.</i> + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public short readShort(final int index) { + byte[] b = this.b; + return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); + } + + /** + * Reads a signed int value in {@link #b b}. <i>This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters.</i> + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public int readInt(final int index) { + byte[] b = this.b; + return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) + | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); + } + + /** + * Reads a signed long value in {@link #b b}. <i>This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters.</i> + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ + public long readLong(final int index) { + long l1 = readInt(index); + long l0 = readInt(index + 4) & 0xFFFFFFFFL; + return (l1 << 32) | l0; + } + + /** + * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method + * is intended for {@link Attribute} sub classes, and is normally not needed + * by class generators or adapters.</i> + * + * @param index the start index of an unsigned short value in {@link #b b}, + * whose value is the index of an UTF8 constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 item. + */ + public String readUTF8(int index, final char[] buf) { + int item = readUnsignedShort(index); + String s = strings[item]; + if (s != null) { + return s; + } + index = items[item]; + return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); + } + + /** + * Reads UTF8 string in {@link #b b}. + * + * @param index start offset of the UTF8 string to be read. + * @param utfLen length of the UTF8 string to be read. + * @param buf buffer to be used to read the string. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 string. + */ + private String readUTF(int index, int utfLen, char[] buf) { + int endIndex = index + utfLen; + byte[] b = this.b; + int strLen = 0; + int c, d, e; + while (index < endIndex) { + c = b[index++] & 0xFF; + switch (c >> 4) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + buf[strLen++] = (char) c; + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + d = b[index++]; + buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F)); + break; + default: + // 1110 xxxx 10xx xxxx 10xx xxxx + d = b[index++]; + e = b[index++]; + buf[strLen++] = (char) (((c & 0x0F) << 12) + | ((d & 0x3F) << 6) | (e & 0x3F)); + break; + } + } + return new String(buf, 0, strLen); + } + + /** + * Reads a class constant pool item in {@link #b b}. <i>This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters.</i> + * + * @param index the start index of an unsigned short value in {@link #b b}, + * whose value is the index of a class constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified class item. + */ + public String readClass(final int index, final char[] buf) { + // computes the start index of the CONSTANT_Class item in b + // and reads the CONSTANT_Utf8 item designated by + // the first two bytes of this CONSTANT_Class item + return readUTF8(items[readUnsignedShort(index)], buf); + } + + /** + * Reads a numeric or string constant pool item in {@link #b b}. <i>This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters.</i> + * + * @param item the index of a constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the {@link Integer}, {@link Float}, {@link Long}, + * {@link Double}, {@link String} or {@link Type} corresponding to + * the given constant pool item. + */ + 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)); + case ClassWriter.FLOAT: + return new Float(Float.intBitsToFloat(readInt(index))); + case ClassWriter.LONG: + return new Long(readLong(index)); + case ClassWriter.DOUBLE: + return new Double(Double.longBitsToDouble(readLong(index))); + case ClassWriter.CLASS: + String s = readUTF8(index, buf); + return Type.getType(s.charAt(0) == '[' ? s : "L" + s + ";"); + case ClassWriter.STR: + return readUTF8(index, buf); + case ClassWriter.MTYPE: + return Type.getMethodType(readUTF8(index, buf)); + default: // case ClassWriter.HANDLE_BASE + [1..9]: + int tag = readByte(index); + int[] items = this.items; + int cpIndex = items[readUnsignedShort(index + 1)]; + 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); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/ClassVisitor.java b/asmx/src/org/objectweb/asm/ClassVisitor.java new file mode 100644 index 0000000..a6e12a0 --- /dev/null +++ b/asmx/src/org/objectweb/asm/ClassVisitor.java
@@ -0,0 +1,195 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A visitor to visit a Java class. The methods of this interface must be called + * in the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [ + * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> | + * <tt>visitAttribute</tt> )* (<tt>visitInnerClass</tt> | + * <tt>visitField</tt> | <tt>visitMethod</tt> )* <tt>visitEnd</tt>. + * + * @author Eric Bruneton + */ +public interface ClassVisitor extends MemberVisitor { + + /** + * Visits the header of the class. + * + * @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 Type#getInternalName() getInternalName}). + * @param signature the signature of this class. May be <tt>null</tt> if + * the class is not a generic one, and does not extend or implement + * generic classes or interfaces. + * @param superName the internal of name of the super class (see + * {@link Type#getInternalName() getInternalName}). For interfaces, + * the super class is {@link Object}. May be <tt>null</tt>, but + * only for the {@link Object} class. + * @param interfaces the internal names of the class's interfaces (see + * {@link Type#getInternalName() getInternalName}). May be + * <tt>null</tt>. + */ + void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces); + + /** + * Visits the source of the class. + * + * @param source the name of the source file from which the class was + * compiled. May be <tt>null</tt>. + * @param debug additional debug information to compute the correspondance + * between source and compiled elements of the class. May be + * <tt>null</tt>. + */ + void visitSource(String source, String debug); + + /** + * 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 + * <tt>null</tt> 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 + * <tt>null</tt> if the class is not enclosed in a method of its + * enclosing class. + */ + void visitOuterClass(String owner, String name, String desc); + + /** + * Visits an annotation of the class. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a non null visitor to visit the annotation values. + */ + AnnotationVisitor visitAnnotation(String desc, boolean visible); + + /** + * Visits a non standard attribute of the class. + * + * @param attr an attribute. + */ + void visitAttribute(Attribute attr); + + /** + * Visits information about an inner class. This inner class is not + * necessarily a member of the class being visited. + * + * @param name the internal name of an inner class (see + * {@link Type#getInternalName() getInternalName}). + * @param outerName the internal name of the class to which the inner class + * belongs (see {@link Type#getInternalName() getInternalName}). May + * be <tt>null</tt>. + * @param innerName the (simple) name of the inner class inside its + * enclosing class. May be <tt>null</tt> for anonymous inner + * classes. + * @param access the access flags of the inner class as originally declared + * in the enclosing class. + */ + void visitInnerClass( + String name, + String outerName, + String innerName, + int access); + + /** + * Visits a field of the class. + * + * @param access the field's access flags (see {@link Opcodes}). This + * parameter also indicates if the field is synthetic and/or + * deprecated. + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type Type}). + * @param signature the field's signature. May be <tt>null</tt> if the + * field's type does not use generic types. + * @param value the field's initial value. This parameter, which may be + * <tt>null</tt> 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 <tt>int</tt>, + * <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields + * respectively). <i>This parameter is only used for static fields</i>. + * Its value is ignored for non static fields, which must be + * initialized through bytecode instructions in constructors or + * methods. + * @return a visitor to visit field annotations and attributes, or + * <tt>null</tt> if this class visitor is not interested in + * visiting these annotations and attributes. + */ + FieldVisitor visitField( + int access, + String name, + String desc, + String signature, + Object value); + + /** + * Visits a method of the class. This method <i>must</i> return a new + * {@link MethodVisitor} instance (or <tt>null</tt>) each time it is + * called, i.e., it should not return a previously returned visitor. + * + * @param access the method's access flags (see {@link Opcodes}). This + * parameter also indicates if the method is synthetic and/or + * deprecated. + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + * @param signature the method's signature. May be <tt>null</tt> if the + * method parameters, return type and exceptions do not use generic + * types. + * @param exceptions the internal names of the method's exception classes + * (see {@link Type#getInternalName() getInternalName}). May be + * <tt>null</tt>. + * @return an object to visit the byte code of the method, or <tt>null</tt> + * if this class visitor is not interested in visiting the code of + * this method. + */ + MethodVisitor visitMethod( + int access, + String name, + String desc, + String signature, + String[] exceptions); + + /** + * Visits the end of the class. This method, which is the last one to be + * called, is used to inform the visitor that all the fields and methods of + * the class have been visited. + */ + void visitEnd(); +}
diff --git a/asmx/src/org/objectweb/asm/ClassWriter.java b/asmx/src/org/objectweb/asm/ClassWriter.java new file mode 100644 index 0000000..f3718c6 --- /dev/null +++ b/asmx/src/org/objectweb/asm/ClassWriter.java
@@ -0,0 +1,1506 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A {@link ClassVisitor} that generates classes in bytecode form. More + * precisely this visitor generates a byte array conforming to the Java class + * file format. It can be used alone, to generate a Java class "from scratch", + * or with one or more {@link ClassReader ClassReader} and adapter class visitor + * to generate a modified class from one or more existing Java classes. + * + * @author Eric Bruneton + */ +public class ClassWriter implements ClassVisitor { + + /** + * The type of instructions without any argument. + */ + final static int NOARG_INSN = 0; + + /** + * The type of instructions with an signed byte argument. + */ + final static int SBYTE_INSN = 1; + + /** + * The type of instructions with an signed short argument. + */ + final static int SHORT_INSN = 2; + + /** + * The type of instructions with a local variable index argument. + */ + final static int VAR_INSN = 3; + + /** + * The type of instructions with an implicit local variable index argument. + */ + final static int IMPLVAR_INSN = 4; + + /** + * The type of instructions with a type descriptor argument. + */ + final static int TYPE_INSN = 5; + + /** + * The type of field and method invocations instructions. + */ + final static int FIELDORMETH_INSN = 6; + + /** + * The type of the INVOKEINTERFACE instruction. + */ + final static int ITFMETH_INSN = 7; + + /** + * The type of instructions with a 2 bytes bytecode offset label. + */ + final static int LABEL_INSN = 8; + + /** + * The type of instructions with a 4 bytes bytecode offset label. + */ + final static int LABELW_INSN = 9; + + /** + * The type of the LDC instruction. + */ + final static int LDC_INSN = 10; + + /** + * The type of the LDC_W and LDC2_W instructions. + */ + final static int LDCW_INSN = 11; + + /** + * The type of the IINC instruction. + */ + final static int IINC_INSN = 12; + + /** + * The type of the TABLESWITCH instruction. + */ + final static int TABL_INSN = 13; + + /** + * The type of the LOOKUPSWITCH instruction. + */ + final static int LOOK_INSN = 14; + + /** + * The type of the MULTIANEWARRAY instruction. + */ + final static int MANA_INSN = 15; + + /** + * The type of the WIDE instruction. + */ + final static int WIDE_INSN = 16; + + /** + * The instruction types of all JVM opcodes. + */ + static final byte[] TYPE; + + /** + * The type of CONSTANT_Class constant pool items. + */ + static final int CLASS = 7; + + /** + * The type of CONSTANT_Fieldref constant pool items. + */ + static final int FIELD = 9; + + /** + * The type of CONSTANT_Methodref constant pool items. + */ + static final int METH = 10; + + /** + * The type of CONSTANT_InterfaceMethodref constant pool items. + */ + static final int IMETH = 11; + + /** + * The type of CONSTANT_String constant pool items. + */ + static final int STR = 8; + + /** + * The type of CONSTANT_Integer constant pool items. + */ + static final int INT = 3; + + /** + * The type of CONSTANT_Float constant pool items. + */ + static final int FLOAT = 4; + + /** + * The type of CONSTANT_Long constant pool items. + */ + static final int LONG = 5; + + /** + * The type of CONSTANT_Double constant pool items. + */ + static final int DOUBLE = 6; + + /** + * The type of CONSTANT_NameAndType constant pool items. + */ + static final int NAME_TYPE = 12; + + /** + * The type of CONSTANT_Utf8 constant pool items. + */ + static final int UTF8 = 1; + + /** + * The type of CONSTANT_MethodType constant pool items. + */ + static final int MTYPE = 16; + + /** + * The type of CONSTANT_MethodHandle constant pool items. + */ + static final int HANDLE = 15; + + /** + * The type of CONSTANT_InvokeDynamic constant pool items. + */ + static final int INDY = 18; + + /** + * The base value for all CONSTANT_MethodHandle constant pool items. + * Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9 + * different items. + */ + static final int HANDLE_BASE = 20; + + /** + * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ + static final int TYPE_NORMAL = 30; + + /** + * Uninitialized type Item stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. + */ + static final int TYPE_UNINIT = 31; + + /** + * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ + static final int TYPE_MERGED = 32; + + /** + * The type of BootstrapMethods items. These items are stored in a special + * class attribute named BootstrapMethods and not in the constant pool. + */ + static final int BSM = 33; + + /** + * The class reader from which this class writer was constructed, if any. + */ + ClassReader cr; + + /** + * Minor and major version numbers of the class to be generated. + */ + int version; + + /** + * Index of the next item to be added in the constant pool. + */ + int index; + + /** + * The constant pool of this class. + */ + ByteVector pool; + + /** + * The constant pool's hash table data. + */ + Item[] items; + + /** + * The threshold of the constant pool's hash table. + */ + int threshold; + + /** + * A reusable key used to look for items in the hash {@link #items items}. + */ + Item key; + + /** + * A reusable key used to look for items in the hash {@link #items items}. + */ + Item key2; + + /** + * A reusable key used to look for items in the hash {@link #items items}. + */ + Item key3; + + /** + * A reusable key used to look for items in the hash {@link #items items}. + */ + Item key4; + + /** + * The access flags of this class. + */ + private int access; + + /** + * The constant pool item that contains the internal name of this class. + */ + private int name; + + /** + * The constant pool item that contains the signature of this class. + */ + private int signature; + + /** + * The constant pool item that contains the internal name of the super class + * of this class. + */ + private int superName; + + /** + * Number of interfaces implemented or extended by this class or interface. + */ + private int interfaceCount; + + /** + * The interfaces implemented or extended by this class or interface. More + * precisely, this array contains the indexes of the constant pool items + * that contain the internal names of these interfaces. + */ + private int[] interfaces; + + /** + * The index of the constant pool item that contains the name of the source + * file from which this class was compiled. + */ + private int sourceFile; + + /** + * The SourceDebug attribute of this class. + */ + private ByteVector sourceDebug; + + /** + * The constant pool item that contains the name of the enclosing class of + * this class. + */ + private int enclosingMethodOwner; + + /** + * The constant pool item that contains the name and descriptor of the + * enclosing method of this class. + */ + private int enclosingMethod; + + /** + * The runtime visible annotations of this class. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this class. + */ + private AnnotationWriter ianns; + + //jaime + private TypeAnnotationWriter xanns; + private TypeAnnotationWriter ixanns; + //end jaime + + /** + * The non standard attributes of this class. + */ + private Attribute attrs; + + /** + * The number of entries in the InnerClasses attribute. + */ + private int innerClassesCount; + + /** + * The InnerClasses attribute. + */ + private ByteVector innerClasses; + + /** + * The number of entries in the BootstrapMethods attribute. + */ + int bootstrapMethodsCount; + + /** + * The BootstrapMethods attribute. + */ + ByteVector bootstrapMethods; + + /** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#next} field. This field stores the first element of + * this list. + */ + FieldWriter firstField; + + /** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#next} field. This field stores the last element of + * this list. + */ + FieldWriter lastField; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#next} field. This field stores the first element of + * this list. + */ + MethodWriter firstMethod; + + /** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#next} field. This field stores the last element of + * this list. + */ + MethodWriter lastMethod; + + /** + * <tt>true</tt> if the maximum stack size and number of local variables + * must be automatically computed. + */ + private final boolean computeMaxs; + + // ------------------------------------------------------------------------ + // Static initializer + // ------------------------------------------------------------------------ + + /** + * Computes the instruction types of JVM opcodes. + */ + static { + int i; + byte[] b = new byte[220]; + String s = "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" + + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAA" + + "AAAAGGGGGGGHSFBFAAFFAAQPIIJJIIIIIIIIIIIIIIIIII"; + for (i = 0; i < b.length; ++i) { + b[i] = (byte) (s.charAt(i) - 'A'); + } + TYPE = b; + + // code to generate the above string + // + // // SBYTE_INSN instructions + // b[Constants.NEWARRAY] = SBYTE_INSN; + // b[Constants.BIPUSH] = SBYTE_INSN; + // + // // SHORT_INSN instructions + // b[Constants.SIPUSH] = SHORT_INSN; + // + // // (IMPL)VAR_INSN instructions + // b[Constants.RET] = VAR_INSN; + // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 + // b[i] = IMPLVAR_INSN; + // } + // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 + // b[i] = IMPLVAR_INSN; + // } + // + // // TYPE_INSN instructions + // b[Constants.NEW] = TYPE_INSN; + // b[Constants.ANEWARRAY] = TYPE_INSN; + // b[Constants.CHECKCAST] = TYPE_INSN; + // b[Constants.INSTANCEOF] = TYPE_INSN; + // + // // (Set)FIELDORMETH_INSN instructions + // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { + // b[i] = FIELDORMETH_INSN; + // } + // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; + // + // b[Constants.INVOKEDYNAMIC] = INDY; + // + // // LABEL(W)_INSN instructions + // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { + // b[i] = LABEL_INSN; + // } + // b[Constants.IFNULL] = LABEL_INSN; + // b[Constants.IFNONNULL] = LABEL_INSN; + // b[200] = LABELW_INSN; // GOTO_W + // b[201] = LABELW_INSN; // JSR_W + // // temporary opcodes used internally by ASM - see Label and + // MethodWriter + // for (i = 202; i < 220; ++i) { + // b[i] = LABEL_INSN; + // } + // + // // LDC(_W) instructions + // b[Constants.LDC] = LDC_INSN; + // b[19] = LDCW_INSN; // LDC_W + // b[20] = LDCW_INSN; // LDC2_W + // + // // special instructions + // b[Constants.IINC] = IINC_INSN; + // b[Constants.TABLESWITCH] = TABL_INSN; + // b[Constants.LOOKUPSWITCH] = LOOK_INSN; + // b[Constants.MULTIANEWARRAY] = MANA_INSN; + // b[196] = WIDE_INSN; // WIDE + // + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('A' + b[i])); + // } + // System.err.println(); + } + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link ClassWriter ClassWriter} object. + * + * @param computeMaxs <tt>true</tt> if the maximum stack size and the + * maximum number of local variables must be automatically computed. + * If this flag is <tt>true</tt>, then the arguments of the + * {@link MethodVisitor#visitMaxs visitMaxs} method of the + * {@link MethodVisitor} returned by the + * {@link #visitMethod visitMethod} method will be ignored, and + * computed automatically from the signature and the bytecode of each + * method. + */ + public ClassWriter(final boolean computeMaxs) { + this(computeMaxs, false); + } + + /** + * Constructs a new {@link ClassWriter} object. + * + * @param computeMaxs <tt>true</tt> if the maximum stack size and the + * maximum number of local variables must be automatically computed. + * If this flag is <tt>true</tt>, then the arguments of the + * {@link MethodVisitor#visitMaxs visitMaxs} method of the + * {@link MethodVisitor} returned by the + * {@link #visitMethod visitMethod} method will be ignored, and + * computed automatically from the signature and the bytecode of each + * method. + * @param skipUnknownAttributes <b>Deprecated</b>. The value of this + * parameter is ignored. + */ + public ClassWriter( + final boolean computeMaxs, + final boolean skipUnknownAttributes) + { + index = 1; + pool = new ByteVector(); + items = new Item[256]; + threshold = (int) (0.75d * items.length); + key = new Item(); + key2 = new Item(); + key3 = new Item(); + key4 = new Item(); + this.computeMaxs = computeMaxs; + } + + /** + * Constructs a new {@link ClassWriter} object and enables optimizations for + * "mostly add" bytecode transformations. These optimizations are the + * following: + * + * <ul> <li>The constant pool from the original class is copied as is in + * the new class, which saves time. New constant pool entries will be added + * at the end if necessary, but unused constant pool entries <i>won't be + * removed</i>.</li> <li>Methods that are not transformed are copied as + * is in the new class, directly from the original class bytecode (i.e. + * without emitting visit events for all the method instructions), which + * saves a <i>lot</i> of time. Untransformed methods are detected by the + * fact that the {@link ClassReader} receives {@link MethodVisitor} objects + * that come from a {@link ClassWriter} (and not from a custom + * {@link ClassAdapter} or any other {@link ClassVisitor} instance).</li> + * </ul> + * + * @param classReader the {@link ClassReader} used to read the original + * class. It will be used to copy the entire constant pool from the + * original class and also to copy other fragments of original + * bytecode where applicable. + * @param computeMaxs <tt>true</tt> if the maximum stack size and the + * maximum number of local variables must be automatically computed. + * If this flag is <tt>true</tt>, then the arguments of the + * {@link MethodVisitor#visitMaxs visitMaxs} method of the + * {@link MethodVisitor} returned by the + * {@link #visitMethod visitMethod} method will be ignored, and + * computed automatically from the signature and the bytecode of each + * method. + */ + public ClassWriter( + final ClassReader classReader, + final boolean computeMaxs) + { + this(computeMaxs, false); + classReader.copyPool(this); + this.cr = classReader; + } + + // ------------------------------------------------------------------------ + // Implementation of the ClassVisitor interface + // ------------------------------------------------------------------------ + + @Override + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + this.version = version; + this.access = access; + this.name = newClass(name); + if (signature != null) { + this.signature = newUTF8(signature); + } + this.superName = superName == null ? 0 : newClass(superName); + if (interfaces != null && interfaces.length > 0) { + interfaceCount = interfaces.length; + this.interfaces = new int[interfaceCount]; + for (int i = 0; i < interfaceCount; ++i) { + this.interfaces[i] = newClass(interfaces[i]); + } + } + } + + @Override + public void visitSource(final String file, final String debug) { + if (file != null) { + sourceFile = newUTF8(file); + } + if (debug != null) { + sourceDebug = new ByteVector().putUTF8(debug); + } + } + + @Override + public void visitOuterClass( + final String owner, + final String name, + final String desc) + { + enclosingMethodOwner = newClass(owner); + if (name != null && desc != null) { + enclosingMethod = newNameType(name, desc); + } + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + //jaime + @Override + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + ByteVector bv = new ByteVector(); + TypeAnnotationWriter xaw = new TypeAnnotationWriter(this, true, bv, bv, desc); + if(visible) { + xaw.next = xanns; + xanns = xaw; + } else { + xaw.next = ixanns; + ixanns = xaw; + } + + return xaw; + } + //end jaime + + @Override + public void visitAttribute(final Attribute attr) { + attr.next = attrs; + attrs = attr; + } + + @Override + public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access) + { + 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); + } + + @Override + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + return new FieldWriter(this, access, name, desc, signature, value); + } + + @Override + public 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); + } + + @Override + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Other public methods + // ------------------------------------------------------------------------ + + /** + * Returns the bytecode of the class that was build with this class writer. + * + * @return the bytecode of the class that was build with this class writer. + */ + public byte[] toByteArray() { + // computes the real size of the bytecode of this class + int size = 24 + 2 * interfaceCount; + int nbFields = 0; + FieldWriter fb = firstField; + while (fb != null) { + ++nbFields; + size += fb.getSize(); + fb = fb.next; + } + int nbMethods = 0; + MethodWriter mb = firstMethod; + while (mb != null) { + ++nbMethods; + size += mb.getSize(); + mb = mb.next; + } + int attributeCount = 0; + if (bootstrapMethods != null) { + // we put it as first attribute in order to improve a bit + // ClassReader.copyBootstrapMethods + ++attributeCount; + size += 8 + bootstrapMethods.length; + newUTF8("BootstrapMethods"); + } + if (signature != 0) { + ++attributeCount; + size += 8; + newUTF8("Signature"); + } + if (sourceFile != 0) { + ++attributeCount; + size += 8; + newUTF8("SourceFile"); + } + if (sourceDebug != null) { + ++attributeCount; + size += sourceDebug.length + 4; + newUTF8("SourceDebugExtension"); + } + if (enclosingMethodOwner != 0) { + ++attributeCount; + size += 10; + newUTF8("EnclosingMethod"); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + size += 6; + newUTF8("Deprecated"); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (version & 0xffff) < Opcodes.V1_5) + { + ++attributeCount; + size += 6; + newUTF8("Synthetic"); + } + if (version == Opcodes.V1_4) { + if ((access & Opcodes.ACC_ANNOTATION) != 0) { + ++attributeCount; + size += 6; + newUTF8("Annotation"); + } + if ((access & Opcodes.ACC_ENUM) != 0) { + ++attributeCount; + size += 6; + newUTF8("Enum"); + } + } + if (innerClasses != null) { + ++attributeCount; + size += 8 + innerClasses.length; + newUTF8("InnerClasses"); + } + if (anns != null) { + ++attributeCount; + size += 8 + anns.getSize(); + newUTF8("RuntimeVisibleAnnotations"); + } + if (ianns != null) { + ++attributeCount; + size += 8 + ianns.getSize(); + newUTF8("RuntimeInvisibleAnnotations"); + } + + if(xanns != null) { + ++attributeCount; + size += 8 + xanns.getSize(); + newUTF8("RuntimeVisibleTypeAnnotations"); + } + if(ixanns != null) { + ++attributeCount; + size += 8 + ixanns.getSize(); + newUTF8("RuntimeInvisibleTypeAnnotations"); + } + + if (attrs != null) { + attributeCount += attrs.getCount(); + size += attrs.getSize(this, null, 0, -1, -1); + } + size += pool.length; + // allocates a byte vector of this size, in order to avoid unnecessary + // arraycopy operations in the ByteVector.enlarge() method + ByteVector out = new ByteVector(size); + out.putInt(0xCAFEBABE).putInt(version); + out.putShort(index).putByteArray(pool.data, 0, pool.length); + out.putShort(access).putShort(name).putShort(superName); + out.putShort(interfaceCount); + for (int i = 0; i < interfaceCount; ++i) { + out.putShort(interfaces[i]); + } + out.putShort(nbFields); + fb = firstField; + while (fb != null) { + fb.put(out); + fb = fb.next; + } + out.putShort(nbMethods); + mb = firstMethod; + while (mb != null) { + mb.put(out); + mb = mb.next; + } + out.putShort(attributeCount); + if (bootstrapMethods != null) { + out.putShort(newUTF8("BootstrapMethods")); + out.putInt(bootstrapMethods.length + 2).putShort( + bootstrapMethodsCount); + out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); + } + if (signature != 0) { + out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); + } + if (sourceFile != 0) { + out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); + } + if (sourceDebug != null) { + int len = sourceDebug.length - 2; + out.putShort(newUTF8("SourceDebugExtension")).putInt(len); + out.putByteArray(sourceDebug.data, 2, len); + } + if (enclosingMethodOwner != 0) { + out.putShort(newUTF8("EnclosingMethod")).putInt(4); + out.putShort(enclosingMethodOwner).putShort(enclosingMethod); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(newUTF8("Deprecated")).putInt(0); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (version & 0xffff) < Opcodes.V1_5) + { + out.putShort(newUTF8("Synthetic")).putInt(0); + } + if (version == Opcodes.V1_4) { + if ((access & Opcodes.ACC_ANNOTATION) != 0) { + out.putShort(newUTF8("Annotation")).putInt(0); + } + if ((access & Opcodes.ACC_ENUM) != 0) { + out.putShort(newUTF8("Enum")).putInt(0); + } + } + if (innerClasses != null) { + out.putShort(newUTF8("InnerClasses")); + out.putInt(innerClasses.length + 2).putShort(innerClassesCount); + out.putByteArray(innerClasses.data, 0, innerClasses.length); + } + if (anns != null) { + out.putShort(newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ianns != null) { + out.putShort(newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if(xanns != null) { + out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); + xanns.put(out); + } + if(ixanns != null) { + out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); + ixanns.put(out); + } + if (attrs != null) { + attrs.put(this, null, 0, -1, -1, out); + } + return out.data; + } + + // ------------------------------------------------------------------------ + // Utility methods: constant pool management + // ------------------------------------------------------------------------ + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * + * @param cst + * the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double}, a {@link String} or a + * {@link Type}. + * @return a new or already existing constant item with the given value. + */ + Item newConstItem(final Object cst) { + if (cst instanceof Integer) { + int val = ((Integer) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Byte) { + int val = ((Byte) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Character) { + int val = ((Character) cst).charValue(); + return newInteger(val); + } else if (cst instanceof Short) { + int val = ((Short) cst).intValue(); + return newInteger(val); + } else if (cst instanceof Boolean) { + int val = ((Boolean) cst).booleanValue() ? 1 : 0; + 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 newClassItem(t.getInternalName()); + } else if (s == Type.METHOD) { + return newMethodTypeItem(t.getDescriptor()); + } else { // s == primitive type or array + return newClassItem(t.getDescriptor()); + } + } else if (cst instanceof Handle) { + Handle h = (Handle) cst; + return newHandleItem(h.tag, h.owner, h.name, h.desc); + } else { + throw new IllegalArgumentException("value " + cst); + } + } + + /** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param cst the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double} or a {@link String}. + * @return the index of a new or already existing constant item with the + * given value. + */ + public int newConst(final Object cst) { + return newConstItem(cst).index; + } + + /** + * Adds an UTF8 string to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. <i>This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters.</i> + * + * @param value the String value. + * @return the index of a new or already existing UTF8 item. + */ + public int newUTF8(final String value) { + key.set(UTF8, value, null, null); + Item result = get(key); + if (result == null) { + pool.putByte(UTF8).putUTF8(value); + result = new Item(index++, key); + put(result); + } + return result.index; + } + + /** + * Adds a class reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param value the internal name of the class. + * @return the index of a new or already existing class reference item. + */ + public int newClass(final String value) { + return newClassItem(value).index; + } + + /** + * Adds a class reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param value the internal name of the class. + * @return a new or already existing class reference item. + */ + private Item newClassItem(final String value) { + key2.set(CLASS, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(CLASS, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a method type reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param methodDesc + * method descriptor of the method type. + * @return a new or already existing method type reference item. + */ + Item newMethodTypeItem(final String methodDesc) { + key2.set(MTYPE, methodDesc, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(MTYPE, newUTF8(methodDesc)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a method type reference to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param methodDesc + * method descriptor of the method type. + * @return the index of a new or already existing method type reference + * item. + */ + public int newMethodType(final String methodDesc) { + return newMethodTypeItem(methodDesc).index; + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. <i>This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters.</i> + * + * @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. + * @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) { + key4.set(HANDLE_BASE + tag, owner, name, desc); + Item result = get(key4); + if (result == null) { + if (tag <= Opcodes.H_PUTSTATIC) { + put112(HANDLE, tag, newField(owner, name, desc)); + } else { + put112(HANDLE, + tag, + newMethod(owner, name, desc, + tag == Opcodes.H_INVOKEINTERFACE)); + } + result = new Item(index++, key4); + put(result); + } + return result; + } + + /** + * Adds a handle to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. <i>This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters.</i> + * + * @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. + * @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) { + return newHandleItem(tag, owner, name, desc).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. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param name + * name of the invoked method. + * @param desc + * descriptor of the invoke method. + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the bootstrap method constant arguments. + * + * @return a new or an already existing invokedynamic type reference item. + */ + Item newInvokeDynamicItem(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + // cache for performance + ByteVector bootstrapMethods = this.bootstrapMethods; + if (bootstrapMethods == null) { + bootstrapMethods = this.bootstrapMethods = new ByteVector(); + } + + int position = bootstrapMethods.length; // record current position + + int hashCode = bsm.hashCode(); + bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name, + bsm.desc)); + + int argsLength = bsmArgs.length; + bootstrapMethods.putShort(argsLength); + + for (int i = 0; i < argsLength; i++) { + Object bsmArg = bsmArgs[i]; + hashCode ^= bsmArg.hashCode(); + bootstrapMethods.putShort(newConst(bsmArg)); + } + + byte[] data = bootstrapMethods.data; + int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments) + hashCode &= 0x7FFFFFFF; + Item result = items[hashCode % items.length]; + loop: while (result != null) { + if (result.type != BSM || result.hashCode != hashCode) { + result = result.next; + continue; + } + + // because the data encode the size of the argument + // we don't need to test if these size are equals + int resultPosition = result.intVal; + for (int p = 0; p < length; p++) { + if (data[position + p] != data[resultPosition + p]) { + result = result.next; + continue loop; + } + } + break; + } + + int bootstrapMethodIndex; + if (result != null) { + bootstrapMethodIndex = result.index; + bootstrapMethods.length = position; // revert to old position + } else { + bootstrapMethodIndex = bootstrapMethodsCount++; + result = new Item(bootstrapMethodIndex); + result.set(position, hashCode); + put(result); + } + + // now, create the InvokeDynamic constant + key3.set(name, desc, bootstrapMethodIndex); + result = get(key3); + if (result == null) { + put122(INDY, bootstrapMethodIndex, newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds a field reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param owner the internal name of the field's owner class. + * @param name the field's name. + * @param desc the field's descriptor. + * @return the index of a new or already existing field reference item. + */ + public int newField(final String owner, final String name, final String desc) + { + key3.set(FIELD, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(FIELD, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result.index; + } + + /** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param desc the method's descriptor. + * @param itf <tt>true</tt> if <tt>owner</tt> is an interface. + * @return a new or already existing method reference item. + */ + Item newMethodItem( + final String owner, + final String name, + final String desc, + final boolean itf) + { + int type = itf ? IMETH : METH; + key3.set(type, owner, name, desc); + Item result = get(key3); + if (result == null) { + put122(type, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; + } + + /** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * <i>This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters.</i> + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param desc the method's descriptor. + * @param itf <tt>true</tt> if <tt>owner</tt> is an interface. + * @return the index of a new or already existing method reference item. + */ + public int newMethod( + final String owner, + final String name, + final String desc, + final boolean itf) + { + return newMethodItem(owner, name, desc, itf).index; + } + + /** + * Adds an integer to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param value the int value. + * @return a new or already existing int item. + */ + Item newInteger(final int value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(INT).putInt(value); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a float to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the float value. + * @return a new or already existing float item. + */ + Item newFloat(final float value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(FLOAT).putInt(Float.floatToIntBits(value)); + result = new Item(index++, key); + put(result); + } + return result; + } + + /** + * Adds a long to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the long value. + * @return a new or already existing long item. + */ + Item newLong(final long value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(LONG).putLong(value); + result = new Item(index, key); + put(result); + index += 2; + } + return result; + } + + /** + * Adds a double to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the double value. + * @return a new or already existing double item. + */ + Item newDouble(final double value) { + key.set(value); + Item result = get(key); + if (result == null) { + pool.putByte(DOUBLE).putLong(Double.doubleToLongBits(value)); + result = new Item(index, key); + put(result); + index += 2; + } + return result; + } + + /** + * Adds a string to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the String value. + * @return a new or already existing string item. + */ + private Item newString(final String value) { + key2.set(STR, value, null, null); + Item result = get(key2); + if (result == null) { + pool.put12(STR, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; + } + + /** + * Adds a name and type to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. <i>This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters.</i> + * + * @param name a name. + * @param desc a type descriptor. + * @return the index of a new or already existing name and type item. + */ + public int newNameType(final String name, final String desc) { + key2.set(NAME_TYPE, name, desc, null); + Item result = get(key2); + if (result == null) { + put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); + result = new Item(index++, key2); + put(result); + } + return result.index; + } + + /** + * Returns the constant pool's hash table item which is equal to the given + * item. + * + * @param key a constant pool item. + * @return the constant pool's hash table item which is equal to the given + * item, or <tt>null</tt> if there is no such item. + */ + private Item get(final Item key) { + Item i = items[key.hashCode % items.length]; + while (i != null && !key.isEqualTo(i)) { + i = i.next; + } + return i; + } + + /** + * Puts the given item in the constant pool's hash table. The hash table + * <i>must</i> not already contains this item. + * + * @param i the item to be added to the constant pool's hash table. + */ + private void put(final Item i) { + if (index > threshold) { + int ll = items.length; + int nl = ll * 2 + 1; + Item[] newItems = new Item[nl]; + for (int l = ll - 1; l >= 0; --l) { + Item j = items[l]; + while (j != null) { + int index = j.hashCode % newItems.length; + Item k = j.next; + j.next = newItems[index]; + newItems[index] = j; + j = k; + } + } + items = newItems; + threshold = (int) (nl * 0.75); + } + int index = i.hashCode % items.length; + i.next = items[index]; + items[index] = i; + } + + /** + * Puts two bytes and one short into the constant pool. + * + * @param b a byte. + * @param s1 a short. + * @param s2 another short. + */ + private void put112(final int b1, final int b2, final int s) { + pool.put11(b1, b2).putShort(s); + } + + /** + * Puts one byte and two shorts into the constant pool. + * + * @param b a byte. + * @param s1 a short. + * @param s2 another short. + */ + private void put122(final int b, final int s1, final int s2) { + pool.put12(b, s1).putShort(s2); + } +}
diff --git a/asmx/src/org/objectweb/asm/Edge.java b/asmx/src/org/objectweb/asm/Edge.java new file mode 100644 index 0000000..c0bf108 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Edge.java
@@ -0,0 +1,57 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An edge in the control flow graph of a method body. See {@link Label Label}. + * + * @author Eric Bruneton + */ +class Edge { + + /** + * The (relative) stack size in the basic block from which this edge + * originates. This size is equal to the stack size at the "jump" + * instruction to which this edge corresponds, relatively to the stack size + * at the beginning of the originating basic block. + */ + int stackSize; + + /** + * The successor block of the basic block from which this edge originates. + */ + Label successor; + + /** + * The next edge in the list of successors of the originating basic block. + * See {@link Label#successors successors}. + */ + Edge next; +}
diff --git a/asmx/src/org/objectweb/asm/FieldVisitor.java b/asmx/src/org/objectweb/asm/FieldVisitor.java new file mode 100644 index 0000000..078aa56 --- /dev/null +++ b/asmx/src/org/objectweb/asm/FieldVisitor.java
@@ -0,0 +1,62 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A visitor to visit a Java field. The methods of this interface must be called + * in the following order: ( <tt>visitAnnotation</tt> | + * <tt>visitAttribute</tt> )* <tt>visitEnd</tt>. + * + * @author Eric Bruneton + */ +public interface FieldVisitor extends MemberVisitor { + /** + * Visits an annotation of the field. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a non null visitor to visit the annotation values. + */ + AnnotationVisitor visitAnnotation(String desc, boolean visible); + + /** + * Visits a non standard attribute of the field. + * + * @param attr an attribute. + */ + void visitAttribute(Attribute attr); + + /** + * Visits the end of the field. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the field have been visited. + */ + void visitEnd(); +}
diff --git a/asmx/src/org/objectweb/asm/FieldWriter.java b/asmx/src/org/objectweb/asm/FieldWriter.java new file mode 100644 index 0000000..6a193c8 --- /dev/null +++ b/asmx/src/org/objectweb/asm/FieldWriter.java
@@ -0,0 +1,332 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An {@link FieldVisitor} that generates Java fields in bytecode form. + * + * @author Eric Bruneton + */ +final class FieldWriter implements FieldVisitor { + + /** + * Next field writer (see {@link ClassWriter#firstField firstField}). + */ + FieldWriter next; + + /** + * The class writer to which this field must be added. + */ + private ClassWriter cw; + + /** + * Access flags of this field. + */ + private int access; + + /** + * The index of the constant pool item that contains the name of this + * method. + */ + private int name; + + /** + * The index of the constant pool item that contains the descriptor of this + * field. + */ + private int desc; + + /** + * The index of the constant pool item that contains the signature of this + * field. + */ + private int signature; + + /** + * The index of the constant pool item that contains the constant value of + * this field. + */ + private int value; + + /** + * The runtime visible annotations of this field. May be <tt>null</tt>. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this field. May be <tt>null</tt>. + */ + private AnnotationWriter ianns; + + //jaime + private TypeAnnotationWriter xanns; + private TypeAnnotationWriter ixanns; + //end jaime + + /** + * The non standard attributes of this field. May be <tt>null</tt>. + */ + private Attribute attrs; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link FieldWriter}. + * + * @param cw the class writer to which this field must be added. + * @param access the field's access flags (see {@link Opcodes}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type}). + * @param signature the field's signature. May be <tt>null</tt>. + * @param value the field's constant value. May be <tt>null</tt>. + */ + protected FieldWriter( + final ClassWriter cw, + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + if (cw.firstField == null) { + cw.firstField = this; + } else { + cw.lastField.next = this; + } + cw.lastField = this; + this.cw = cw; + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + if (signature != null) { + this.signature = cw.newUTF8(signature); + } + if (value != null) { + this.value = cw.newConstItem(value).index; + } + } + + // ------------------------------------------------------------------------ + // Implementation of the FieldVisitor interface + // ------------------------------------------------------------------------ + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + //jaime + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + ByteVector bv = new ByteVector(); + TypeAnnotationWriter xaw = + new TypeAnnotationWriter(cw, true, bv, bv, desc); + if(visible) { + xaw.next = xanns; + xanns = xaw; + } else { + xaw.next = ixanns; + ixanns = xaw; + } + return xaw; + } + // end jaime + + public void visitAttribute(final Attribute attr) { + attr.next = attrs; + attrs = attr; + } + + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Returns the size of this field. + * + * @return the size of this field. + */ + int getSize() { + int size = 8; + if (value != 0) { + cw.newUTF8("ConstantValue"); + size += 8; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + cw.newUTF8("Synthetic"); + size += 6; + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + if (cw.version == Opcodes.V1_4 && (access & Opcodes.ACC_ENUM) != 0) { + cw.newUTF8("Enum"); + size += 6; + } + if (signature != 0) { + cw.newUTF8("Signature"); + size += 8; + } + if (anns != null) { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if (ianns != null) { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + //jaime + if (xanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + xanns.getSize(); + } + if (ixanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + ixanns.getSize(); + } + // end jaime + if (attrs != null) { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; + } + + /** + * Puts the content of this field into the given byte vector. + * + * @param out where the content of this field must be put. + */ + void put(final ByteVector out) { + out.putShort(access).putShort(name).putShort(desc); + int attributeCount = 0; + if (value != 0) { + ++attributeCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + ++attributeCount; + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (cw.version == Opcodes.V1_4 && (access & Opcodes.ACC_ENUM) != 0) { + ++attributeCount; + } + if (signature != 0) { + ++attributeCount; + } + if (anns != null) { + ++attributeCount; + } + if (ianns != null) { + ++attributeCount; + } + //jaime + if (xanns != null) { + ++attributeCount; + } + if (ixanns != null) { + ++attributeCount; + } + // end jaime + + if (attrs != null) { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if (value != 0) { + out.putShort(cw.newUTF8("ConstantValue")); + out.putInt(2).putShort(value); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if (cw.version == Opcodes.V1_4 && (access & Opcodes.ACC_ENUM) != 0) { + out.putShort(cw.newUTF8("Enum")).putInt(0); + } + if (signature != 0) { + out.putShort(cw.newUTF8("Signature")); + out.putInt(2).putShort(signature); + } + if (anns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ianns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + + //jaime + if (xanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + xanns.put(out); + } + if (ixanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + ixanns.put(out); + } + // end jaime + + if (attrs != null) { + attrs.put(cw, null, 0, -1, -1, out); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/Handle.java b/asmx/src/org/objectweb/asm/Handle.java new file mode 100644 index 0000000..a627911 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Handle.java
@@ -0,0 +1,170 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.objectweb.asm; + +/** + * A reference to a field or a method. + * + * @author Remi Forax + * @author Eric Bruneton + */ +public final class Handle { + + /** + * The kind of field or method designated by this Handle. Should be + * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + */ + final int tag; + + /** + * The internal name of the class that owns the field or method designated + * by this handle. + */ + final String owner; + + /** + * The name of the field or method designated by this handle. + */ + final String name; + + /** + * The descriptor of the field or method designated by this handle. + */ + final String desc; + + /** + * 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. + */ + public Handle(int tag, String owner, String name, String desc) { + this.tag = tag; + this.owner = owner; + this.name = name; + this.desc = desc; + } + + /** + * Returns the kind of field or method designated by this handle. + * + * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, + * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, + * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, + * {@link Opcodes#H_INVOKESPECIAL}, + * {@link Opcodes#H_NEWINVOKESPECIAL} or + * {@link Opcodes#H_INVOKEINTERFACE}. + */ + public int getTag() { + return tag; + } + + /** + * Returns the internal name of the class that owns the field or method + * designated by this handle. + * + * @return the internal name of the class that owns the field or method + * designated by this handle. + */ + public String getOwner() { + return owner; + } + + /** + * Returns the name of the field or method designated by this handle. + * + * @return the name of the field or method designated by this handle. + */ + public String getName() { + return name; + } + + /** + * Returns the descriptor of the field or method designated by this handle. + * + * @return the descriptor of the field or method designated by this handle. + */ + public String getDesc() { + return desc; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Handle)) { + return false; + } + Handle h = (Handle) obj; + return tag == h.tag && owner.equals(h.owner) && name.equals(h.name) + && desc.equals(h.desc); + } + + @Override + public int hashCode() { + return tag + owner.hashCode() * name.hashCode() * desc.hashCode(); + } + + /** + * Returns the textual representation of this handle. The textual + * representation is: + * + * <pre> + * owner '.' name desc ' ' '(' tag ')' + * </pre> + * + * . As this format is unambiguous, it can be parsed if necessary. + */ + @Override + public String toString() { + return owner + '.' + name + desc + " (" + tag + ')'; + } +}
diff --git a/asmx/src/org/objectweb/asm/Handler.java b/asmx/src/org/objectweb/asm/Handler.java new file mode 100644 index 0000000..8bef845 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Handler.java
@@ -0,0 +1,70 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * Information about an exception handler block. + * + * @author Eric Bruneton + */ +class Handler { + + /** + * Beginning of the exception handler's scope (inclusive). + */ + Label start; + + /** + * End of the exception handler's scope (exclusive). + */ + Label end; + + /** + * Beginning of the exception handler's code. + */ + Label handler; + + /** + * Internal name of the type of exceptions handled by this handler, or + * <tt>null</tt> to catch any exceptions. + */ + String desc; + + /** + * Constant pool index of the internal name of the type of exceptions + * handled by this handler, or 0 to catch any exceptions. + */ + int type; + + /** + * Next exception handler block info. + */ + Handler next; +}
diff --git a/asmx/src/org/objectweb/asm/Item.java b/asmx/src/org/objectweb/asm/Item.java new file mode 100644 index 0000000..ed14416 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Item.java
@@ -0,0 +1,325 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A constant pool item. Constant pool items can be created with the 'newXXX' + * methods in the {@link ClassWriter} class. + * + * @author Eric Bruneton + */ +final class Item { + + /** + * Index of this item in the constant pool. + */ + int index; + + /** + * 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 one of {@link ClassWriter#INT}, + * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, + * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, + * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, + * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, + * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}, + * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}. + * + * MethodHandle constant 9 variations are stored using a range of 9 values + * from {@link ClassWriter#HANDLE_BASE} + 1 to + * {@link ClassWriter#HANDLE_BASE} + 9. + * + * Special Item types are used for Items that are stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. These special item types are + * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and + * {@link ClassWriter#TYPE_MERGED}. + */ + int 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. + */ + String strVal3; + + /** + * The hash code value of this constant pool item. + */ + int hashCode; + + /** + * Link to another constant pool item, used for collision lists in the + * constant pool's hash table. + */ + Item next; + + /** + * Constructs an uninitialized {@link Item}. + */ + Item() { + } + + /** + * Constructs an uninitialized {@link Item} for constant pool element at + * given position. + * + * @param index + * index of the item to be constructed. + */ + Item(final int index) { + this.index = index; + } + + /** + * Constructs a copy of the given item. + * + * @param index index of the item to be constructed. + * @param i the item that must be copied into the item to be constructed. + */ + Item(final int index, final Item i) { + this.index = index; + type = i.type; + intVal = i.intVal; + longVal = i.longVal; + floatVal = i.floatVal; + doubleVal = i.doubleVal; + strVal1 = i.strVal1; + strVal2 = i.strVal2; + strVal3 = i.strVal3; + hashCode = i.hashCode; + } + + /** + * Sets this item to an integer item. + * + * @param intVal the value of this item. + */ + void set(final int intVal) { + this.type = ClassWriter.INT; + 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 = ClassWriter.LONG; + 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 = ClassWriter.FLOAT; + 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 = ClassWriter.DOUBLE; + 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. + */ + @SuppressWarnings("fallthrough") + void set(final int type, final String strVal1, final String strVal2, + final String strVal3) { + this.type = type; + this.strVal1 = strVal1; + 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.MTYPE: + case ClassWriter.TYPE_NORMAL: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); + return; + case ClassWriter.NAME_TYPE: { + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode()); + return; + } + // ClassWriter.FIELD: + // ClassWriter.METH: + // ClassWriter.IMETH: + // ClassWriter.HANDLE_BASE + 1..9 + default: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode() * strVal3.hashCode()); + } + } + + /** + * Sets the item to an InvokeDynamic item. + * + * @param name + * invokedynamic's name. + * @param desc + * invokedynamic's desc. + * @param bsmIndex + * zero based index into the class attribute BootrapMethods. + */ + void set(String name, String desc, int bsmIndex) { + this.type = ClassWriter.INDY; + this.longVal = bsmIndex; + this.strVal1 = name; + this.strVal2 = desc; + this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex + * strVal1.hashCode() * strVal2.hashCode()); + } + + /** + * Sets the item to a BootstrapMethod item. + * + * @param position + * position in byte in the class attribute BootrapMethods. + * @param hashCode + * hashcode of the item. This hashcode is processed from the + * hashcode of the bootstrap method and the hashcode of all + * bootstrap arguments. + */ + void set(int position, int hashCode) { + this.type = ClassWriter.BSM; + this.intVal = position; + this.hashCode = hashCode; + } + + /** + * Indicates if the given item is equal to this one. <i>This method assumes + * that the two items have the same {@link #type}</i>. + * + * @param i the item to be compared to this one. Both items must have the + * same {@link #type}. + * @return <tt>true</tt> if the given item if equal to this one, + * <tt>false</tt> otherwise. + */ + boolean isEqualTo(final Item i) { + if (i.type == type) { + switch (type) { + case ClassWriter.INT: + return i.intVal == intVal; + case ClassWriter.LONG: + return i.longVal == longVal; + case ClassWriter.FLOAT: + return i.floatVal == floatVal; + case ClassWriter.DOUBLE: + return i.doubleVal == doubleVal; + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + case ClassWriter.MTYPE: + case ClassWriter.TYPE_NORMAL: + return i.strVal1.equals(strVal1); + case ClassWriter.TYPE_MERGED: + return i.longVal == longVal; + case ClassWriter.TYPE_UNINIT: + return i.intVal == intVal && i.strVal1.equals(strVal1); + case ClassWriter.NAME_TYPE: + return i.strVal1.equals(strVal1) + && i.strVal2.equals(strVal2); + case ClassWriter.INDY: { + return i.longVal == longVal && i.strVal1.equals(strVal1) + && i.strVal2.equals(strVal2); + } + // ClassWriter.FIELD: + // ClassWriter.METH: + // ClassWriter.IMETH: + // ClassWriter.HANDLE_BASE + 1..9 + default: + return i.strVal1.equals(strVal1) + && i.strVal2.equals(strVal2) + && i.strVal3.equals(strVal3); + } + } + return false; + } +}
diff --git a/asmx/src/org/objectweb/asm/Label.java b/asmx/src/org/objectweb/asm/Label.java new file mode 100644 index 0000000..79291f2 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Label.java
@@ -0,0 +1,299 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A label represents a position in the bytecode of a method. Labels are used + * for jump, goto, and switch instructions, and for try catch blocks. + * + * @author Eric Bruneton + */ +public class Label { + + /** + * The line number corresponding to this label, if known. + */ + int line; + + /** + * Indicates if the position of this label is known. + */ + boolean resolved; + + /** + * The position of this label in the code, if known. + */ + int position; + + /** + * If the label position has been updated, after instruction resizing. + */ + boolean resized; + + /** + * Number of forward references to this label, times two. + */ + private int referenceCount; + + /** + * Informations about forward references. Each forward reference is + * described by two consecutive integers in this array: the first one is the + * position of the first byte of the bytecode instruction that contains the + * forward reference, while the second is the position of the first byte of + * the forward reference itself. In fact the sign of the first integer + * indicates if this reference uses 2 or 4 bytes, and its absolute value + * gives the position of the bytecode instruction. + */ + private int[] srcAndRefPositions; + + /* + * Fields for the control flow graph analysis algorithm (used to compute the + * maximum stack size). A control flow graph contains one node per "basic + * block", and one edge per "jump" from one basic block to another. Each + * node (i.e., each basic block) is represented by the Label object that + * corresponds to the first instruction of this basic block. Each node also + * stores the list of it successors in the graph, as a linked list of Edge + * objects. + */ + + /** + * The stack size at the beginning of this basic block. This size is + * initially unknown. It is computed by the control flow analysis algorithm + * (see {@link MethodWriter#visitMaxs visitMaxs}). + */ + int beginStackSize; + + /** + * The (relative) maximum stack size corresponding to this basic block. This + * size is relative to the stack size at the beginning of the basic block, + * i.e., the true maximum stack size is equal to {@link #beginStackSize + * beginStackSize} + {@link #maxStackSize maxStackSize}. + */ + int maxStackSize; + + /** + * The successors of this node in the control flow graph. These successors + * are stored in a linked list of {@link Edge Edge} objects, linked to each + * other by their {@link Edge#next} field. + */ + Edge successors; + + /** + * The next basic block in the basic block stack. See + * {@link MethodWriter#visitMaxs visitMaxs}. + */ + Label next; + + /** + * <tt>true</tt> if this basic block has been pushed in the basic block + * stack. See {@link MethodWriter#visitMaxs visitMaxs}. + */ + boolean pushed; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new label. + */ + public Label() { + } + + // ------------------------------------------------------------------------ + // Methods to compute offsets and to manage forward references + // ------------------------------------------------------------------------ + + /** + * Returns the offset corresponding to this label. This offset is computed + * from the start of the method's bytecode. <i>This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters.</i> + * + * @return the offset corresponding to this label. + * @throws IllegalStateException if this label is not resolved yet. + */ + public int getOffset() { + if (!resolved) { + throw new IllegalStateException("Label offset position has not been resolved yet"); + } + return position; + } + + /** + * Puts a reference to this label in the bytecode of a method. If the + * position of the label is known, the offset is computed and written + * directly. Otherwise, a null offset is written and a new forward reference + * is declared for this label. + * + * @param owner the code writer that calls this method. + * @param out the bytecode of the method. + * @param source the position of first byte of the bytecode instruction that + * contains this label. + * @param wideOffset <tt>true</tt> if the reference must be stored in 4 + * bytes, or <tt>false</tt> if it must be stored with 2 bytes. + * @throws IllegalArgumentException if this label has not been created by + * the given code writer. + */ + void put( + final MethodWriter owner, + final ByteVector out, + final int source, + final boolean wideOffset) + { + if (resolved) { + if (wideOffset) { + out.putInt(position - source); + } else { + out.putShort(position - source); + } + } else { + if (wideOffset) { + addReference(-1 - source, out.length); + out.putInt(-1); + } else { + addReference(source, out.length); + out.putShort(-1); + } + } + } + + /** + * Adds a forward reference to this label. This method must be called only + * for a true forward reference, i.e. only if this label is not resolved + * yet. For backward references, the offset of the reference can be, and + * must be, computed and stored directly. + * + * @param sourcePosition the position of the referencing instruction. This + * position will be used to compute the offset of this forward + * reference. + * @param referencePosition the position where the offset for this forward + * reference must be stored. + */ + private void addReference( + final int sourcePosition, + final int referencePosition) + { + if (srcAndRefPositions == null) { + srcAndRefPositions = new int[6]; + } + if (referenceCount >= srcAndRefPositions.length) { + int[] a = new int[srcAndRefPositions.length + 6]; + System.arraycopy(srcAndRefPositions, + 0, + a, + 0, + srcAndRefPositions.length); + srcAndRefPositions = a; + } + srcAndRefPositions[referenceCount++] = sourcePosition; + srcAndRefPositions[referenceCount++] = referencePosition; + } + + /** + * Resolves all forward references to this label. This method must be called + * when this label is added to the bytecode of the method, i.e. when its + * position becomes known. This method fills in the blanks that where left + * in the bytecode by each forward reference previously added to this label. + * + * @param owner the code writer that calls this method. + * @param position the position of this label in the bytecode. + * @param data the bytecode of the method. + * @return <tt>true</tt> if a blank that was left for this label was to + * 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}. + * @throws IllegalArgumentException if this label has already been resolved, + * or if it has not been created by the given code writer. + */ + boolean resolve( + final MethodWriter owner, + final int position, + final byte[] data) + { + boolean needUpdate = false; + this.resolved = true; + this.position = position; + int i = 0; + while (i < referenceCount) { + int source = srcAndRefPositions[i++]; + int reference = srcAndRefPositions[i++]; + int offset; + if (source >= 0) { + offset = position - source; + if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { + /* + * changes the opcode of the jump instruction, in order to + * be able to find it later (see resizeInstructions in + * MethodWriter). These temporary opcodes are similar to + * jump instruction opcodes, except that the 2 bytes offset + * is unsigned (and can therefore represent values from 0 to + * 65535, which is sufficient since the size of a method is + * limited to 65535 bytes). + */ + int opcode = data[reference - 1] & 0xFF; + if (opcode <= Opcodes.JSR) { + // changes IFEQ ... JSR to opcodes 202 to 217 + data[reference - 1] = (byte) (opcode + 49); + } else { + // changes IFNULL and IFNONNULL to opcodes 218 and 219 + data[reference - 1] = (byte) (opcode + 20); + } + needUpdate = true; + } + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } else { + offset = position + source + 1; + data[reference++] = (byte) (offset >>> 24); + data[reference++] = (byte) (offset >>> 16); + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } + } + return needUpdate; + } + + // ------------------------------------------------------------------------ + // Overriden Object methods + // ------------------------------------------------------------------------ + + /** + * Returns a string representation of this label. + * + * @return a string representation of this label. + */ + public String toString() { + return "L" + System.identityHashCode(this); + } +}
diff --git a/asmx/src/org/objectweb/asm/MemberVisitor.java b/asmx/src/org/objectweb/asm/MemberVisitor.java new file mode 100644 index 0000000..df184bd --- /dev/null +++ b/asmx/src/org/objectweb/asm/MemberVisitor.java
@@ -0,0 +1,19 @@ +package org.objectweb.asm; + +/** + * Shared methods between {@link ClassVisitor}, {@link FieldVisitor}, and + * {@link MethodVisitor}. + */ +public interface MemberVisitor { + + /** + * Visits a type annotation of this member. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @param inCode <tt>true</tt> if this annotation belongs in the <tt>Code</tt> + * attribute, <tt>false</tt> otherwise. This is only used for methods. + * @return a non null visitor to visit the annotation values. + */ + TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode); +}
diff --git a/asmx/src/org/objectweb/asm/MethodAdapter.java b/asmx/src/org/objectweb/asm/MethodAdapter.java new file mode 100644 index 0000000..20587df --- /dev/null +++ b/asmx/src/org/objectweb/asm/MethodAdapter.java
@@ -0,0 +1,214 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * An empty {@link MethodVisitor} that delegates to another + * {@link MethodVisitor}. This class can be used as a super class to quickly + * implement usefull method adapter classes, just by overriding the necessary + * methods. + * + * @author Eric Bruneton + */ +public class MethodAdapter implements MethodVisitor { + + /** + * The {@link MethodVisitor} to which this adapter delegates calls. + */ + protected MethodVisitor mv; + + /** + * Constructs a new {@link MethodAdapter} object. + * + * @param mv the code visitor to which this adapter must delegate calls. + */ + public MethodAdapter(final MethodVisitor mv) { + this.mv = mv; + } + + public AnnotationVisitor visitAnnotationDefault() { + return mv.visitAnnotationDefault(); + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + return mv.visitAnnotation(desc, visible); + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + return mv.visitTypeAnnotation(desc, visible, inCode); + } + //end jaime + + public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible) + { + return mv.visitParameterAnnotation(parameter, desc, visible); + } + + public void visitAttribute(final Attribute attr) { + mv.visitAttribute(attr); + } + + public void visitCode() { + mv.visitCode(); + } + + public void visitInsn(final int opcode) { + mv.visitInsn(opcode); + } + + public void visitIntInsn(final int opcode, final int operand) { + mv.visitIntInsn(opcode, operand); + } + + public void visitVarInsn(final int opcode, final int var) { + mv.visitVarInsn(opcode, var); + } + + public void visitTypeInsn(final int opcode, final String desc) { + mv.visitTypeInsn(opcode, desc); + } + + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + mv.visitFieldInsn(opcode, owner, name, desc); + } + + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + mv.visitMethodInsn(opcode, owner, name, desc); + } + + @Override + public void visitInvokeDynamicInsn( + final String name, + final String desc, + final Handle bsm, + final Object... bsmArgs) + { + mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + } + + public void visitJumpInsn(final int opcode, final Label label) { + mv.visitJumpInsn(opcode, label); + } + + public void visitLabel(final Label label) { + mv.visitLabel(label); + } + + public void visitLdcInsn(final Object cst) { + mv.visitLdcInsn(cst); + } + + public void visitIincInsn(final int var, final int increment) { + mv.visitIincInsn(var, increment); + } + + public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + + public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]) + { + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + + public void visitMultiANewArrayInsn(final String desc, final int dims) { + mv.visitMultiANewArrayInsn(desc, dims); + } + + @Override + public AnnotationVisitor visitInsnAnnotation( + int typeRef, + TypePath typePath, + String desc, + boolean visible) + { + return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); + } + + public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type) + { + mv.visitTryCatchBlock(start, end, handler, type); + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + mv.visitLocalVariable(name, desc, signature, start, end, index); + } + + public void visitLineNumber(final int line, final Label start) { + mv.visitLineNumber(line, start); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + mv.visitMaxs(maxStack, maxLocals); + } + + public void visitEnd() { + mv.visitEnd(); + } +}
diff --git a/asmx/src/org/objectweb/asm/MethodVisitor.java b/asmx/src/org/objectweb/asm/MethodVisitor.java new file mode 100644 index 0000000..5e7e049 --- /dev/null +++ b/asmx/src/org/objectweb/asm/MethodVisitor.java
@@ -0,0 +1,387 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A visitor to visit a Java method. The methods of this interface must be + * called in the following order: [ <tt>visitAnnotationDefault</tt> ] ( + * <tt>visitAnnotation</tt> | <tt>visitParameterAnnotation</tt> | + * <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visit</tt><i>X</i>Insn</tt> | + * <tt>visitLabel</tt> | <tt>visitTryCatchBlock</tt> | <tt>visitLocalVariable</tt> | + * <tt>visitLineNumber</tt>)* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In + * addition, the <tt>visit</tt><i>X</i>Insn</tt> and <tt>visitLabel</tt> + * methods must be called in the sequential order of the bytecode instructions + * of the visited code, and the <tt>visitLocalVariable</tt> and <tt>visitLineNumber</tt> + * methods must be called <i>after</i> the labels passed as arguments have been + * visited. + * + * @author Eric Bruneton + */ +public interface MethodVisitor extends MemberVisitor { + + // ------------------------------------------------------------------------- + // Annotations and non standard attributes + // ------------------------------------------------------------------------- + + /** + * Visits the default value of this annotation interface method. + * + * @return a non null visitor to the visit the actual default value of this + * annotation interface method. The 'name' parameters passed to the + * methods of this annotation visitor are ignored. Moreover, exacly + * one visit method must be called on this annotation visitor, + * followed by visitEnd. + */ + AnnotationVisitor visitAnnotationDefault(); + + /** + * Visits an annotation of this method. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a non null visitor to visit the annotation values. + */ + AnnotationVisitor visitAnnotation(String desc, boolean visible); + + /** + * Visits an annotation of a parameter this method. + * + * @param parameter the parameter index. + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a non null visitor to visit the annotation values. + */ + AnnotationVisitor visitParameterAnnotation( + int parameter, + String desc, + boolean visible); + + /** + * Visits a non standard attribute of this method. + * + * @param attr an attribute. + */ + void visitAttribute(Attribute attr); + + /** + * Starts the visit of the method's code, if any (i.e. non abstract method). + */ + void visitCode(); + + // ------------------------------------------------------------------------- + // Normal instructions + // ------------------------------------------------------------------------- + + /** + * Visits a zero operand instruction. + * + * @param opcode the opcode of the instruction to be visited. This opcode is + * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, + * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, + * FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD, + * DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, + * DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, + * DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD, + * DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, + * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, + * LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, + * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, + * I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, + * FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, + * MONITORENTER, or MONITOREXIT. + */ + void visitInsn(int opcode); + + /** + * Visits an instruction with a single int operand. + * + * @param opcode the opcode of the instruction to be visited. This opcode is + * either BIPUSH, SIPUSH or NEWARRAY. + * @param operand the operand of the instruction to be visited.<br> + * When opcode is BIPUSH, operand value should be between + * Byte.MIN_VALUE and Byte.MAX_VALUE.<br> + * When opcode is SIPUSH, operand value should be between + * Short.MIN_VALUE and Short.MAX_VALUE.<br> + * 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}. + */ + void visitIntInsn(int opcode, int operand); + + /** + * Visits a local variable instruction. A local variable instruction is an + * instruction that loads or stores the value of a local variable. + * + * @param opcode the opcode of the local variable instruction to be visited. + * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, + * LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var the operand of the instruction to be visited. This operand is + * the index of a local variable. + */ + void visitVarInsn(int opcode, int var); + + /** + * Visits a type instruction. A type instruction is an instruction that + * takes a type descriptor as parameter. + * + * @param opcode the opcode of the type instruction to be visited. This + * opcode is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param desc the operand of the instruction to be visited. This operand is + * must be a fully qualified class name in internal form, or the type + * descriptor of an array type (see {@link Type Type}). + */ + void visitTypeInsn(int opcode, String desc); + + /** + * Visits a field instruction. A field instruction is an instruction that + * loads or stores the value of a field of an object. + * + * @param opcode the opcode of the type instruction to be visited. This + * opcode is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner the internal name of the field's owner class (see {@link + * Type#getInternalName() getInternalName}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type Type}). + */ + void visitFieldInsn(int opcode, String owner, String name, String 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}). + */ + void visitMethodInsn(int opcode, String owner, String name, String desc); + + /** + * Visits an invokedynamic instruction. + * + * @param name + * the method's name. + * @param desc + * the method's descriptor (see {@link 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 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. + */ + void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs); + + /** + * Visits a jump instruction. A jump instruction is an instruction that may + * jump to another instruction. + * + * @param opcode the opcode of the type instruction to be visited. This + * opcode is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, + * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, + * IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. + * @param label the operand of the instruction to be visited. This operand + * is a label that designates the instruction to which the jump + * instruction may jump. + */ + void visitJumpInsn(int opcode, Label label); + + /** + * Visits a label. A label designates the instruction that will be visited + * just after it. + * + * @param label a {@link Label Label} object. + */ + void visitLabel(Label label); + + // ------------------------------------------------------------------------- + // Special instructions + // ------------------------------------------------------------------------- + + /** + * Visits a LDC instruction. + * + * @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} (or a {@link Type} for + * <tt>.class</tt> constants, for classes whose version is 49.0 or + * more). + */ + void visitLdcInsn(Object cst); + + /** + * Visits an IINC instruction. + * + * @param var index of the local variable to be incremented. + * @param increment amount to increment the local variable by. + */ + void visitIincInsn(int var, int increment); + + /** + * Visits a TABLESWITCH instruction. + * + * @param min the minimum key value. + * @param max the maximum key value. + * @param dflt beginning of the default handler block. + * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is + * the beginning of the handler block for the <tt>min + i</tt> key. + */ + void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[]); + + /** + * Visits a LOOKUPSWITCH instruction. + * + * @param dflt beginning of the default handler block. + * @param keys the values of the keys. + * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is + * the beginning of the handler block for the <tt>keys[i]</tt> key. + */ + void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]); + + /** + * Visits a MULTIANEWARRAY instruction. + * + * @param desc an array type descriptor (see {@link Type Type}). + * @param dims number of dimensions of the array to allocate. + */ + void visitMultiANewArrayInsn(String desc, int dims); + + /** + * Visits an annotation on an instruction. This method must be called just + * <i>after</i> 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 + * <tt>null</tt> if the annotation targets 'typeRef' as a whole. + * @param desc + * the class descriptor of the annotation class. + * @param visible + * <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or <tt>null</tt> if + * this visitor is not interested in visiting this annotation. + */ + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible); + + // ------------------------------------------------------------------------- + // Exceptions table entries, debug information, + // max stack size and max locals + // ------------------------------------------------------------------------- + + /** + * Visits a try catch block. + * + * @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 <tt>null</tt> to catch any exceptions (for "finally" + * blocks). + */ + void visitTryCatchBlock(Label start, Label end, Label handler, String type); + + /** + * Visits a local variable declaration. + * + * @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 + * <tt>null</tt> 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). + */ + void visitLocalVariable( + String name, + String desc, + String signature, + Label start, + Label end, + int index); + + /** + * Visits a line number declaration. + * + * @param line a line number. This number refers to the source file from + * which the class was compiled. + * @param start the first instruction corresponding to this line number. + * @throws IllegalArgumentException if <tt>start</tt> has not already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ + void visitLineNumber(int line, Label start); + + /** + * Visits the maximum stack size and the maximum number of local variables + * of the method. + * + * @param maxStack maximum stack size of the method. + * @param maxLocals maximum number of local variables for the method. + */ + void visitMaxs(int maxStack, int maxLocals); + + /** + * Visits the end of the method. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the method have been visited. + */ + void visitEnd(); +}
diff --git a/asmx/src/org/objectweb/asm/MethodWriter.java b/asmx/src/org/objectweb/asm/MethodWriter.java new file mode 100644 index 0000000..84efabd --- /dev/null +++ b/asmx/src/org/objectweb/asm/MethodWriter.java
@@ -0,0 +1,2201 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * A {@link MethodVisitor} that generates methods in bytecode form. Each visit + * method of this class appends the bytecode corresponding to the visited + * instruction to a byte vector, in the order these methods are called. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +class MethodWriter implements MethodVisitor { + + /** + * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). + */ + MethodWriter next; + + /** + * The class writer to which this method must be added. + */ + ClassWriter cw; + + /** + * Access flags of this method. + */ + private int access; + + /** + * The index of the constant pool item that contains the name of this + * method. + */ + private int name; + + /** + * The index of the constant pool item that contains the descriptor of this + * method. + */ + private int desc; + + /** + * The descriptor of this method. + */ + private String descriptor; + + /** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in <code>cw.cr</code>. More + * precisely, this field gives the index of the first byte to copied from + * <code>cw.cr.b</code>. + */ + int classReaderOffset; + + /** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in <code>cw.cr</code>. More + * precisely, this field gives the number of bytes to copied from + * <code>cw.cr.b</code>. + */ + int classReaderLength; + + /** + * The signature of this method. + */ + String signature; + + /** + * Number of exceptions that can be thrown by this method. + */ + int exceptionCount; + + /** + * The exceptions that can be thrown by this method. More precisely, this + * array contains the indexes of the constant pool items that contain the + * internal names of these exception classes. + */ + int[] exceptions; + + /** + * The annotation default attribute of this method. May be <tt>null</tt>. + */ + private ByteVector annd; + + /** + * The runtime visible annotations of this method. May be <tt>null</tt>. + */ + private AnnotationWriter anns; + + /** + * The runtime invisible annotations of this method. May be <tt>null</tt>. + */ + private AnnotationWriter ianns; + + //jaime + /** + * The runtime visible type annotations of this method. May be + * <tt>null</tt>. + */ + private TypeAnnotationWriter xanns; + + /** + * The runtime invisible type annotations of this method. May be + * <tt>null</tt>. + */ + private TypeAnnotationWriter ixanns; + //end jaime + + /** + * The runtime visible type annotations of this method that belong in the + * <tt>Code</tt> attribute. May be <tt>null</tt>. + */ + private TypeAnnotationWriter cxanns; + + /** + * The runtime invisible type annotations of this method that belong in the + * <tt>Code</tt> attribute. May be <tt>null</tt>. + */ + private TypeAnnotationWriter cixanns; + + /** + * The runtime visible parameter annotations of this method. May be + * <tt>null</tt>. + */ + private AnnotationWriter[] panns; + + /** + * The runtime invisible parameter annotations of this method. May be + * <tt>null</tt>. + */ + private AnnotationWriter[] ipanns; + + /** + * The non standard attributes of the method. + */ + private Attribute attrs; + + /** + * The bytecode of this method. + */ + private ByteVector code = new ByteVector(); + + /** + * Maximum stack size of this method. + */ + private int maxStack; + + /** + * Maximum number of local variables for this method. + */ + private int maxLocals; + + /** + * Number of entries in the catch table of this method. + */ + private int catchCount; + + /** + * The catch table of this method. + */ + private Handler catchTable; + + /** + * The last element in the catchTable handler list. + */ + private Handler lastHandler; + + /** + * Number of entries in the LocalVariableTable attribute. + */ + private int localVarCount; + + /** + * The LocalVariableTable attribute. + */ + private ByteVector localVar; + + /** + * Number of entries in the LocalVariableTypeTable attribute. + */ + private int localVarTypeCount; + + /** + * The LocalVariableTypeTable attribute. + */ + private ByteVector localVarType; + + /** + * Number of entries in the LineNumberTable attribute. + */ + private int lineNumberCount; + + /** + * The LineNumberTable attribute. + */ + private ByteVector lineNumber; + + /** + * The start offset of the last visited instruction. + */ + private int lastCodeOffset; + + /** + * The non standard attributes of the method's code. + */ + private Attribute cattrs; + + /** + * Indicates if some jump instructions are too small and need to be resized. + */ + private boolean resize; + + /* + * Fields for the control flow graph analysis algorithm (used to compute the + * maximum stack size). A control flow graph contains one node per "basic + * block", and one edge per "jump" from one basic block to another. Each + * node (i.e., each basic block) is represented by the Label object that + * corresponds to the first instruction of this basic block. Each node also + * stores the list of its successors in the graph, as a linked list of Edge + * objects. + */ + + /** + * <tt>true</tt> if the maximum stack size and number of local variables + * must be automatically computed. + */ + private final boolean computeMaxs; + + /** + * The (relative) stack size after the last visited instruction. This size + * is relative to the beginning of the current basic block, i.e., the true + * stack size after the last visited instruction is equal to the {@link + * Label#beginStackSize beginStackSize} of the current basic block plus + * <tt>stackSize</tt>. + */ + private int stackSize; + + /** + * The (relative) maximum stack size after the last visited instruction. + * This size is relative to the beginning of the current basic block, i.e., + * the true maximum stack size after the last visited instruction is equal + * to the {@link Label#beginStackSize beginStackSize} of the current basic + * block plus <tt>stackSize</tt>. + */ + private int maxStackSize; + + /** + * The current basic block. This block is the basic block to which the next + * instruction to be visited must be added. + */ + private Label currentBlock; + + /** + * The basic block stack used by the control flow analysis algorithm. This + * stack is represented by a linked list of {@link Label Label} objects, + * linked to each other by their {@link Label#next} field. This stack must + * not be confused with the JVM stack used to execute the JVM instructions! + */ + private Label blockStack; + + /** + * The stack size variation corresponding to each JVM instruction. This + * stack variation is equal to the size of the values produced by an + * instruction, minus the size of the values consumed by this instruction. + */ + private final static int[] SIZE; + + // ------------------------------------------------------------------------ + // Static initializer + // ------------------------------------------------------------------------ + + /** + * Computes the stack size variation corresponding to each JVM instruction. + */ + static { + int i; + int[] b = new int[202]; + String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" + + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" + + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" + + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; + for (i = 0; i < b.length; ++i) { + b[i] = s.charAt(i) - 'E'; + } + SIZE = b; + + // code to generate the above string + // + // int NA = 0; // not applicable (unused opcode or variable size opcode) + // + // b = new int[] { + // 0, //NOP, // visitInsn + // 1, //ACONST_NULL, // - + // 1, //ICONST_M1, // - + // 1, //ICONST_0, // - + // 1, //ICONST_1, // - + // 1, //ICONST_2, // - + // 1, //ICONST_3, // - + // 1, //ICONST_4, // - + // 1, //ICONST_5, // - + // 2, //LCONST_0, // - + // 2, //LCONST_1, // - + // 1, //FCONST_0, // - + // 1, //FCONST_1, // - + // 1, //FCONST_2, // - + // 2, //DCONST_0, // - + // 2, //DCONST_1, // - + // 1, //BIPUSH, // visitIntInsn + // 1, //SIPUSH, // - + // 1, //LDC, // visitLdcInsn + // NA, //LDC_W, // - + // NA, //LDC2_W, // - + // 1, //ILOAD, // visitVarInsn + // 2, //LLOAD, // - + // 1, //FLOAD, // - + // 2, //DLOAD, // - + // 1, //ALOAD, // - + // NA, //ILOAD_0, // - + // NA, //ILOAD_1, // - + // NA, //ILOAD_2, // - + // NA, //ILOAD_3, // - + // NA, //LLOAD_0, // - + // NA, //LLOAD_1, // - + // NA, //LLOAD_2, // - + // NA, //LLOAD_3, // - + // NA, //FLOAD_0, // - + // NA, //FLOAD_1, // - + // NA, //FLOAD_2, // - + // NA, //FLOAD_3, // - + // NA, //DLOAD_0, // - + // NA, //DLOAD_1, // - + // NA, //DLOAD_2, // - + // NA, //DLOAD_3, // - + // NA, //ALOAD_0, // - + // NA, //ALOAD_1, // - + // NA, //ALOAD_2, // - + // NA, //ALOAD_3, // - + // -1, //IALOAD, // visitInsn + // 0, //LALOAD, // - + // -1, //FALOAD, // - + // 0, //DALOAD, // - + // -1, //AALOAD, // - + // -1, //BALOAD, // - + // -1, //CALOAD, // - + // -1, //SALOAD, // - + // -1, //ISTORE, // visitVarInsn + // -2, //LSTORE, // - + // -1, //FSTORE, // - + // -2, //DSTORE, // - + // -1, //ASTORE, // - + // NA, //ISTORE_0, // - + // NA, //ISTORE_1, // - + // NA, //ISTORE_2, // - + // NA, //ISTORE_3, // - + // NA, //LSTORE_0, // - + // NA, //LSTORE_1, // - + // NA, //LSTORE_2, // - + // NA, //LSTORE_3, // - + // NA, //FSTORE_0, // - + // NA, //FSTORE_1, // - + // NA, //FSTORE_2, // - + // NA, //FSTORE_3, // - + // NA, //DSTORE_0, // - + // NA, //DSTORE_1, // - + // NA, //DSTORE_2, // - + // NA, //DSTORE_3, // - + // NA, //ASTORE_0, // - + // NA, //ASTORE_1, // - + // NA, //ASTORE_2, // - + // NA, //ASTORE_3, // - + // -3, //IASTORE, // visitInsn + // -4, //LASTORE, // - + // -3, //FASTORE, // - + // -4, //DASTORE, // - + // -3, //AASTORE, // - + // -3, //BASTORE, // - + // -3, //CASTORE, // - + // -3, //SASTORE, // - + // -1, //POP, // - + // -2, //POP2, // - + // 1, //DUP, // - + // 1, //DUP_X1, // - + // 1, //DUP_X2, // - + // 2, //DUP2, // - + // 2, //DUP2_X1, // - + // 2, //DUP2_X2, // - + // 0, //SWAP, // - + // -1, //IADD, // - + // -2, //LADD, // - + // -1, //FADD, // - + // -2, //DADD, // - + // -1, //ISUB, // - + // -2, //LSUB, // - + // -1, //FSUB, // - + // -2, //DSUB, // - + // -1, //IMUL, // - + // -2, //LMUL, // - + // -1, //FMUL, // - + // -2, //DMUL, // - + // -1, //IDIV, // - + // -2, //LDIV, // - + // -1, //FDIV, // - + // -2, //DDIV, // - + // -1, //IREM, // - + // -2, //LREM, // - + // -1, //FREM, // - + // -2, //DREM, // - + // 0, //INEG, // - + // 0, //LNEG, // - + // 0, //FNEG, // - + // 0, //DNEG, // - + // -1, //ISHL, // - + // -1, //LSHL, // - + // -1, //ISHR, // - + // -1, //LSHR, // - + // -1, //IUSHR, // - + // -1, //LUSHR, // - + // -1, //IAND, // - + // -2, //LAND, // - + // -1, //IOR, // - + // -2, //LOR, // - + // -1, //IXOR, // - + // -2, //LXOR, // - + // 0, //IINC, // visitIincInsn + // 1, //I2L, // visitInsn + // 0, //I2F, // - + // 1, //I2D, // - + // -1, //L2I, // - + // -1, //L2F, // - + // 0, //L2D, // - + // 0, //F2I, // - + // 1, //F2L, // - + // 1, //F2D, // - + // -1, //D2I, // - + // 0, //D2L, // - + // -1, //D2F, // - + // 0, //I2B, // - + // 0, //I2C, // - + // 0, //I2S, // - + // -3, //LCMP, // - + // -1, //FCMPL, // - + // -1, //FCMPG, // - + // -3, //DCMPL, // - + // -3, //DCMPG, // - + // -1, //IFEQ, // visitJumpInsn + // -1, //IFNE, // - + // -1, //IFLT, // - + // -1, //IFGE, // - + // -1, //IFGT, // - + // -1, //IFLE, // - + // -2, //IF_ICMPEQ, // - + // -2, //IF_ICMPNE, // - + // -2, //IF_ICMPLT, // - + // -2, //IF_ICMPGE, // - + // -2, //IF_ICMPGT, // - + // -2, //IF_ICMPLE, // - + // -2, //IF_ACMPEQ, // - + // -2, //IF_ACMPNE, // - + // 0, //GOTO, // - + // 1, //JSR, // - + // 0, //RET, // visitVarInsn + // -1, //TABLESWITCH, // visiTableSwitchInsn + // -1, //LOOKUPSWITCH, // visitLookupSwitch + // -1, //IRETURN, // visitInsn + // -2, //LRETURN, // - + // -1, //FRETURN, // - + // -2, //DRETURN, // - + // -1, //ARETURN, // - + // 0, //RETURN, // - + // NA, //GETSTATIC, // visitFieldInsn + // NA, //PUTSTATIC, // - + // NA, //GETFIELD, // - + // NA, //PUTFIELD, // - + // NA, //INVOKEVIRTUAL, // visitMethodInsn + // NA, //INVOKESPECIAL, // - + // NA, //INVOKESTATIC, // - + // NA, //INVOKEINTERFACE, // - + // NA, //UNUSED, // NOT VISITED + // 1, //NEW, // visitTypeInsn + // 0, //NEWARRAY, // visitIntInsn + // 0, //ANEWARRAY, // visitTypeInsn + // 0, //ARRAYLENGTH, // visitInsn + // NA, //ATHROW, // - + // 0, //CHECKCAST, // visitTypeInsn + // 0, //INSTANCEOF, // - + // -1, //MONITORENTER, // visitInsn + // -1, //MONITOREXIT, // - + // NA, //WIDE, // NOT VISITED + // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn + // -1, //IFNULL, // visitJumpInsn + // -1, //IFNONNULL, // - + // NA, //GOTO_W, // - + // NA, //JSR_W, // - + // }; + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('E' + b[i])); + // } + // System.err.println(); + } + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link MethodWriter}. + * + * @param cw the class writer in which the method must be added. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type}). + * @param signature the method's signature. May be <tt>null</tt>. + * @param exceptions the internal names of the method's exceptions. May be + * <tt>null</tt>. + * @param computeMaxs <tt>true</tt> if the maximum stack size and number + * of local variables must be automatically computed. + */ + MethodWriter( + final ClassWriter cw, + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions, + final boolean computeMaxs) + { + if (cw.firstMethod == null) { + cw.firstMethod = this; + } else { + cw.lastMethod.next = this; + } + cw.lastMethod = this; + this.cw = cw; + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + this.descriptor = desc; + this.signature = signature; + if (exceptions != null && exceptions.length > 0) { + exceptionCount = exceptions.length; + this.exceptions = new int[exceptionCount]; + for (int i = 0; i < exceptionCount; ++i) { + this.exceptions[i] = cw.newClass(exceptions[i]); + } + } + this.computeMaxs = computeMaxs; + if (computeMaxs) { + // updates maxLocals + int size = getArgumentsAndReturnSizes(desc) >> 2; + if ((access & Opcodes.ACC_STATIC) != 0) { + --size; + } + maxLocals = size; + // pushes the first block onto the stack of blocks to be visited + currentBlock = new Label(); + currentBlock.pushed = true; + blockStack = currentBlock; + } + } + + // ------------------------------------------------------------------------ + // Implementation of the MethodVisitor interface + // ------------------------------------------------------------------------ + + public AnnotationVisitor visitAnnotationDefault() { + annd = new ByteVector(); + return new AnnotationWriter(cw, false, annd, null, 0); + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + aw.next = anns; + anns = aw; + } else { + aw.next = ianns; + ianns = aw; + } + return aw; + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + ByteVector bv = new ByteVector(); + TypeAnnotationWriter xaw = + new TypeAnnotationWriter(cw, true, bv, bv, desc); + if (inCode) { + if (visible) { + xaw.next = cxanns; + cxanns = xaw; + } else { + xaw.next = cixanns; + cixanns = xaw; + } + } else { + if (visible) { + xaw.next = xanns; + xanns = xaw; + } else { + xaw.next = ixanns; + ixanns = xaw; + } + } + return xaw; + } + //end jaime + + public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible) + { + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if (visible) { + if (panns == null) { + panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = panns[parameter]; + panns[parameter] = aw; + } else { + if (ipanns == null) { + ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = ipanns[parameter]; + ipanns[parameter] = aw; + } + return aw; + } + + public void visitAttribute(final Attribute attr) { + if (attr.isCodeAttribute()) { + attr.next = cattrs; + cattrs = attr; + } else { + attr.next = attrs; + attrs = attr; + } + } + + public void visitCode() { + } + + public void visitInsn(final int opcode) { + if (computeMaxs) { + // updates current and max stack sizes + int size = stackSize + SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + // if opcode == ATHROW or xRETURN, ends current block (no successor) + if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) + || opcode == Opcodes.ATHROW) + { + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + currentBlock = null; + } + } + } + // adds the instruction to the bytecode of the method + code.putByte(opcode); + } + + public void visitIntInsn(final int opcode, final int operand) { + if (computeMaxs && opcode != Opcodes.NEWARRAY) { + // updates current and max stack sizes only if opcode == NEWARRAY + // (stack size variation = 0 for BIPUSH or SIPUSH) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + if (opcode == Opcodes.SIPUSH) { + code.put12(opcode, operand); + } else { // BIPUSH or NEWARRAY + code.put11(opcode, operand); + } + } + + public void visitVarInsn(final int opcode, final int var) { + if (computeMaxs) { + // updates current and max stack sizes + if (opcode == Opcodes.RET) { + // no stack change, but end of current block (no successor) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + currentBlock = null; + } + } else { // xLOAD or xSTORE + int size = stackSize + SIZE[opcode]; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // updates max locals + int n; + if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD + || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) + { + n = var + 2; + } else { + n = var + 1; + } + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if (var < 4 && opcode != Opcodes.RET) { + int opt; + if (opcode < Opcodes.ISTORE) { + /* ILOAD_0 */ + opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; + } else { + /* ISTORE_0 */ + opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; + } + code.putByte(opt); + } else if (var >= 256) { + code.putByte(196 /* WIDE */).put12(opcode, var); + } else { + code.put11(opcode, var); + } + } + + public void visitTypeInsn(final int opcode, final String desc) { + if (computeMaxs && opcode == Opcodes.NEW) { + // updates current and max stack sizes only if opcode == NEW + // (stack size variation = 0 for ANEWARRAY, CHECKCAST, INSTANCEOF) + int size = stackSize + 1; + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + code.put12(opcode, cw.newClass(desc)); + } + + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + if (computeMaxs) { + int size; + // computes the stack size variation + char c = desc.charAt(0); + switch (opcode) { + case Opcodes.GETSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); + break; + case Opcodes.PUTSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); + break; + case Opcodes.GETFIELD: + size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); + break; + // case Constants.PUTFIELD: + default: + size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); + break; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + code.put12(opcode, cw.newField(owner, name, desc)); + } + + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + Item i = cw.newMethodItem(owner, name, desc, itf); + int argSize = i.intVal; + if (computeMaxs) { + /* + * computes the stack size variation. In order not to recompute + * several times this variation for the same Item, we use the intVal + * field of this item to store this variation, once it has been + * computed. More precisely this intVal field stores the sizes of + * the arguments and of the return value corresponding to desc. + */ + if (argSize == 0) { + // the above sizes have not been computed yet, so we compute + // them... + argSize = getArgumentsAndReturnSizes(desc); + // ... and we save them in order not to recompute them in the + // future + i.intVal = argSize; + } + int size; + if (opcode == Opcodes.INVOKESTATIC) { + size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + } else { + size = stackSize - (argSize >> 2) + (argSize & 0x03); + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + if (itf) { + if (!computeMaxs) { + if (argSize == 0) { + argSize = getArgumentsAndReturnSizes(desc); + i.intVal = argSize; + } + } + code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); + } else { + code.put12(opcode, i.index); + } + } + + public void visitInvokeDynamicInsn(int ix1, int ix2) { + code.putByte(ix1); + code.putByte(ix2); + } + + @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) { + // currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i); + //} else { + /* + * computes the stack size variation. In order not to recompute + * several times this variation for the same Item, we use the + * intVal field of this item to store this variation, once it + * has been computed. More precisely this intVal field stores + * the sizes of the arguments and of the return value + * corresponding to desc. + */ + if (argSize == 0) { + // the above sizes have not been computed yet, + // so we compute them... + argSize = Type.getArgumentsAndReturnSizes(desc); + // ... and we save them in order + // not to recompute them in the future + i.intVal = argSize; + } + int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + //} + } + // adds the instruction to the bytecode of the method + code.put12(Opcodes.INVOKEDYNAMIC, i.index); + code.putShort(0); + } + + public void visitJumpInsn(final int opcode, final Label label) { + if (computeMaxs) { + if (opcode == Opcodes.GOTO) { + // no stack change, but end of current block (with one new + // successor) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, label); + currentBlock = null; + } + } else if (opcode == Opcodes.JSR) { + if (currentBlock != null) { + addSuccessor(stackSize + 1, label); + } + } else { + // updates current stack size (max stack size unchanged because + // stack size variation always negative in this case) + stackSize += SIZE[opcode]; + if (currentBlock != null) { + addSuccessor(stackSize, label); + } + } + } + // adds the instruction to the bytecode of the method + if (label.resolved && label.position - code.length < Short.MIN_VALUE) { + /* + * case of a backward jump with an offset < -32768. In this case we + * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx + * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the + * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'> + * designates the instruction just after the GOTO_W. + */ + if (opcode == Opcodes.GOTO) { + code.putByte(200); // GOTO_W + } else if (opcode == Opcodes.JSR) { + code.putByte(201); // JSR_W + } else { + code.putByte(opcode <= 166 + ? ((opcode + 1) ^ 1) - 1 + : opcode ^ 1); + code.putShort(8); // jump offset + code.putByte(200); // GOTO_W + } + label.put(this, code, code.length - 1, true); + } else { + /* + * case of a backward jump with an offset >= -32768, or of a forward + * jump with, of course, an unknown offset. In these cases we store + * the offset in 2 bytes (which will be increased in + * resizeInstructions, if needed). + */ + code.putByte(opcode); + label.put(this, code, code.length - 1, false); + } + } + + public void visitLabel(final Label label) { + if (computeMaxs) { + if (currentBlock != null) { + // ends current block (with one new successor) + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, label); + } + // begins a new current block, + // resets the relative current and max stack sizes + currentBlock = label; + stackSize = 0; + maxStackSize = 0; + } + // resolves previous forward references to label, if any + resize |= label.resolve(this, code.length, code.data); + } + + public void visitLdcInsn(final Object cst) { + Item i = cw.newConstItem(cst); + if (computeMaxs) { + int size; + // computes the stack size variation + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + size = stackSize + 2; + } else { + size = stackSize + 1; + } + // updates current and max stack sizes + if (size > maxStackSize) { + maxStackSize = size; + } + stackSize = size; + } + // adds the instruction to the bytecode of the method + int index = i.index; + if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { + code.put12(20 /* LDC2_W */, index); + } else if (index >= 256) { + code.put12(19 /* LDC_W */, index); + } else { + code.put11(Opcodes.LDC, index); + } + } + + public void visitIincInsn(final int var, final int increment) { + if (computeMaxs) { + // updates max locals only (no stack change) + int n = var + 1; + if (n > maxLocals) { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if ((var > 255) || (increment > 127) || (increment < -128)) { + code.putByte(196 /* WIDE */) + .put12(Opcodes.IINC, var) + .putShort(increment); + } else { + code.putByte(Opcodes.IINC).put11(var, increment); + } + } + + public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + if (computeMaxs) { + // updates current stack size (max stack size unchanged) + --stackSize; + // ends current block (with many new successors) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + currentBlock = null; + } + } + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.TABLESWITCH); + while (code.length % 4 != 0) { + code.putByte(0); + } + dflt.put(this, code, source, true); + code.putInt(min).putInt(max); + for (int i = 0; i < labels.length; ++i) { + labels[i].put(this, code, source, true); + } + } + + public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]) + { + if (computeMaxs) { + // updates current stack size (max stack size unchanged) + --stackSize; + // ends current block (with many new successors) + if (currentBlock != null) { + currentBlock.maxStackSize = maxStackSize; + addSuccessor(stackSize, dflt); + for (int i = 0; i < labels.length; ++i) { + addSuccessor(stackSize, labels[i]); + } + currentBlock = null; + } + } + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.LOOKUPSWITCH); + while (code.length % 4 != 0) { + code.putByte(0); + } + dflt.put(this, code, source, true); + code.putInt(labels.length); + for (int i = 0; i < labels.length; ++i) { + code.putInt(keys[i]); + labels[i].put(this, code, source, true); + } + } + + public void visitMultiANewArrayInsn(final String desc, final int dims) { + if (computeMaxs) { + // updates current stack size (max stack size unchanged because + // stack size variation always negative or null) + stackSize += 1 - dims; + } + // adds the instruction to the bytecode of the method + code.put12(Opcodes.MULTIANEWARRAY, cw.newClass(desc)).putByte(dims); + } + + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + ByteVector bv = new ByteVector(); + // write target_type and target_info + typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8); + // TODO: 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); + // TODO: + //if (visible) { + // aw.next = ctanns; + // ctanns = aw; + //} else { + // aw.next = ictanns; + // ictanns = aw; + //} + return aw; + } + + public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type) + { + if (computeMaxs) { + // pushes handler block onto the stack of blocks to be visited + if (!handler.pushed) { + handler.beginStackSize = 1; + handler.pushed = true; + handler.next = blockStack; + blockStack = handler; + } + } + ++catchCount; + Handler h = new Handler(); + h.start = start; + h.end = end; + h.handler = handler; + h.desc = type; + h.type = type != null ? cw.newClass(type) : 0; + if (lastHandler == null) { + catchTable = h; + } else { + lastHandler.next = h; + } + lastHandler = h; + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + if (signature != null) { + if (localVarType == null) { + localVarType = new ByteVector(); + } + ++localVarTypeCount; + localVarType.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)) + .putShort(cw.newUTF8(signature)) + .putShort(index); + } + if (localVar == null) { + localVar = new ByteVector(); + } + ++localVarCount; + localVar.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)) + .putShort(cw.newUTF8(desc)) + .putShort(index); + + if(computeMaxs) { + // updates max locals + char c = desc.charAt(0); + int n = index + ( c=='L' || c=='D' ? 2 : 1); + if (n > maxLocals) { + maxLocals = n; + } + } + } + + public void visitLineNumber(final int line, final Label start) { + if (lineNumber == null) { + lineNumber = new ByteVector(); + } + ++lineNumberCount; + lineNumber.putShort(start.position); + lineNumber.putShort(line); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + if (computeMaxs) { + // true (non relative) max stack size + int max = 0; + /* + * control flow analysis algorithm: while the block stack is not + * empty, pop a block from this stack, update the max stack size, + * compute the true (non relative) begin stack size of the + * successors of this block, and push these successors onto the + * stack (unless they have already been pushed onto the stack). + * Note: by hypothesis, the {@link Label#beginStackSize} of the + * blocks in the block stack are the true (non relative) beginning + * stack sizes of these blocks. + */ + Label stack = blockStack; + while (stack != null) { + // pops a block from the stack + Label l = stack; + stack = stack.next; + // computes the true (non relative) max stack size of this block + int start = l.beginStackSize; + int blockMax = start + l.maxStackSize; + // updates the global max stack size + if (blockMax > max) { + max = blockMax; + } + // analyses the successors of the block + Edge b = l.successors; + while (b != null) { + l = b.successor; + // if this successor has not already been pushed onto the + // stack... + if (!l.pushed) { + // computes the true beginning stack size of this + // successor block + l.beginStackSize = start + b.stackSize; + // pushes this successor onto the stack + l.pushed = true; + l.next = stack; + stack = l; + } + b = b.next; + } + } + this.maxStack = max; + } else { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } + } + + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Utility methods: control flow analysis algorithm + // ------------------------------------------------------------------------ + + /** + * Computes the size of the arguments and of the return value of a method. + * + * @param desc the descriptor of a 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 = + * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal + * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). + */ + private static int getArgumentsAndReturnSizes(final String desc) { + int n = 1; + int c = 1; + while (true) { + char car = desc.charAt(c++); + if (car == ')') { + car = desc.charAt(c); + return n << 2 + | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); + } else if (car == 'L') { + while (desc.charAt(c++) != ';') { + } + n += 1; + } else if (car == '[') { + while ((car = desc.charAt(c)) == '[') { + ++c; + } + if (car == 'D' || car == 'J') { + n -= 1; + } + } else if (car == 'D' || car == 'J') { + n += 2; + } else { + n += 1; + } + } + } + + /** + * Adds a successor to the {@link #currentBlock currentBlock} block. + * + * @param stackSize the current (relative) stack size in the current block. + * @param successor the successor block to be added to the current block. + */ + private void addSuccessor(final int stackSize, final Label successor) { + Edge b = new Edge(); + // initializes the previous Edge object... + b.stackSize = stackSize; + b.successor = successor; + // ...and adds it to the successor list of the currentBlock block + b.next = currentBlock.successors; + currentBlock.successors = b; + } + + // ------------------------------------------------------------------------ + // Utility methods: dump bytecode array + // ------------------------------------------------------------------------ + + /** + * Returns the size of the bytecode of this method. + * + * @return the size of the bytecode of this method. + */ + final int getSize() { + if (classReaderOffset != 0) { + return 6 + classReaderLength; + } + if (resize) { + // replaces the temporary jump opcodes introduced by Label.resolve. + resizeInstructions(new int[0], new int[0], 0); + } + int size = 8; + if (code.length > 0) { + cw.newUTF8("Code"); + size += 18 + code.length + 8 * catchCount; + if (localVar != null) { + cw.newUTF8("LocalVariableTable"); + size += 8 + localVar.length; + } + if (localVarType != null) { + cw.newUTF8("LocalVariableTypeTable"); + size += 8 + localVarType.length; + } + if (lineNumber != null) { + cw.newUTF8("LineNumberTable"); + size += 8 + lineNumber.length; + } + if (cxanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + cxanns.getSize(); + } + if (cixanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + cixanns.getSize(); + } + if (cattrs != null) { + size += cattrs.getSize(cw, + code.data, + code.length, + maxStack, + maxLocals); + } + } + if (exceptionCount > 0) { + cw.newUTF8("Exceptions"); + size += 8 + 2 * exceptionCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + cw.newUTF8("Synthetic"); + size += 6; + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + cw.newUTF8("Deprecated"); + size += 6; + } + if (cw.version == Opcodes.V1_4) { + if ((access & Opcodes.ACC_VARARGS) != 0) { + cw.newUTF8("Varargs"); + size += 6; + } + if ((access & Opcodes.ACC_BRIDGE) != 0) { + cw.newUTF8("Bridge"); + size += 6; + } + } + if (signature != null) { + cw.newUTF8("Signature"); + cw.newUTF8(signature); + size += 8; + } + if (annd != null) { + cw.newUTF8("AnnotationDefault"); + size += 6 + annd.length; + } + if (anns != null) { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if (ianns != null) { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + if (xanns != null) { + cw.newUTF8("RuntimeVisibleTypeAnnotations"); + size += 8 + xanns.getSize(); + } + if (ixanns != null) { + cw.newUTF8("RuntimeInvisibleTypeAnnotations"); + size += 8 + ixanns.getSize(); + } + if (panns != null) { + cw.newUTF8("RuntimeVisibleParameterAnnotations"); + size += 7 + 2 * panns.length; + for (int i = panns.length - 1; i >= 0; --i) { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + } + if (ipanns != null) { + cw.newUTF8("RuntimeInvisibleParameterAnnotations"); + size += 7 + 2 * ipanns.length; + for (int i = ipanns.length - 1; i >= 0; --i) { + size += ipanns[i] == null ? 0 : ipanns[i].getSize(); + } + } + if (attrs != null) { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; + } + + /** + * Puts the bytecode of this method in the given byte vector. + * + * @param out the byte vector into which the bytecode of this method must be + * copied. + */ + final void put(final ByteVector out) { + out.putShort(access).putShort(name).putShort(desc); + if (classReaderOffset != 0) { + out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); + return; + } + int attributeCount = 0; + if (code.length > 0) { + ++attributeCount; + } + if (exceptionCount > 0) { + ++attributeCount; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + ++attributeCount; + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + ++attributeCount; + } + if (cw.version == Opcodes.V1_4) { + if ((access & Opcodes.ACC_VARARGS) != 0) { + ++attributeCount; + } + if ((access & Opcodes.ACC_BRIDGE) != 0) { + ++attributeCount; + } + } + if (signature != null) { + ++attributeCount; + } + if (annd != null) { + ++attributeCount; + } + if (anns != null) { + ++attributeCount; + } + if (ianns != null) { + ++attributeCount; + } + if (xanns != null) { + ++attributeCount; + } + if (ixanns != null) { + ++attributeCount; + } + if (panns != null) { + ++attributeCount; + } + if (ipanns != null) { + ++attributeCount; + } + if (attrs != null) { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if (code.length > 0) { + int size = 12 + code.length + 8 * catchCount; + if (localVar != null) { + size += 8 + localVar.length; + } + if (localVarType != null) { + size += 8 + localVarType.length; + } + if (lineNumber != null) { + size += 8 + lineNumber.length; + } + if (cxanns != null) { + size += 8 + cxanns.getSize(); + } + if (cixanns != null) { + size += 8 + cixanns.getSize(); + } + if (cattrs != null) { + size += cattrs.getSize(cw, + code.data, + code.length, + maxStack, + maxLocals); + } + out.putShort(cw.newUTF8("Code")).putInt(size); + out.putShort(maxStack).putShort(maxLocals); + out.putInt(code.length).putByteArray(code.data, 0, code.length); + out.putShort(catchCount); + if (catchCount > 0) { + Handler h = catchTable; + while (h != null) { + out.putShort(h.start.position) + .putShort(h.end.position) + .putShort(h.handler.position) + .putShort(h.type); + h = h.next; + } + } + attributeCount = 0; + if (localVar != null) { + ++attributeCount; + } + if (localVarType != null) { + ++attributeCount; + } + if (lineNumber != null) { + ++attributeCount; + } + if (cxanns != null) { + ++attributeCount; + } + if (cixanns != null) { + ++attributeCount; + } + if (cattrs != null) { + attributeCount += cattrs.getCount(); + } + out.putShort(attributeCount); + if (localVar != null) { + out.putShort(cw.newUTF8("LocalVariableTable")); + out.putInt(localVar.length + 2).putShort(localVarCount); + out.putByteArray(localVar.data, 0, localVar.length); + } + if (localVarType != null) { + out.putShort(cw.newUTF8("LocalVariableTypeTable")); + out.putInt(localVarType.length + 2).putShort(localVarTypeCount); + out.putByteArray(localVarType.data, 0, localVarType.length); + } + if (lineNumber != null) { + out.putShort(cw.newUTF8("LineNumberTable")); + out.putInt(lineNumber.length + 2).putShort(lineNumberCount); + out.putByteArray(lineNumber.data, 0, lineNumber.length); + } + if (cxanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + cxanns.put(out); + } + if (cixanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + cixanns.put(out); + } + if (cattrs != null) { + cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); + } + } + if (exceptionCount > 0) { + out.putShort(cw.newUTF8("Exceptions")) + .putInt(2 * exceptionCount + 2); + out.putShort(exceptionCount); + for (int i = 0; i < exceptionCount; ++i) { + out.putShort(exceptions[i]); + } + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if (cw.version == Opcodes.V1_4) { + if ((access & Opcodes.ACC_VARARGS) != 0) { + out.putShort(cw.newUTF8("Varargs")).putInt(0); + } + if ((access & Opcodes.ACC_BRIDGE) != 0) { + out.putShort(cw.newUTF8("Bridge")).putInt(0); + } + } + if (signature != null) { + out.putShort(cw.newUTF8("Signature")) + .putInt(2) + .putShort(cw.newUTF8(signature)); + } + if (annd != null) { + out.putShort(cw.newUTF8("AnnotationDefault")); + out.putInt(annd.length); + out.putByteArray(annd.data, 0, annd.length); + } + if (anns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if (ianns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if (xanns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); + xanns.put(out); + } + if (ixanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); + ixanns.put(out); + } + if (panns != null) { + out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); + AnnotationWriter.put(panns, out); + } + if (ipanns != null) { + out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); + AnnotationWriter.put(ipanns, out); + } + if (attrs != null) { + attrs.put(cw, null, 0, -1, -1, out); + } + } + + // ------------------------------------------------------------------------ + // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) + // ------------------------------------------------------------------------ + + /** + * Resizes the designated instructions, 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. <p> <i>This method must be called after all the method + * that is being built has been visited</i>. In particular, the + * {@link Label Label} objects used to construct the method are no longer + * valid after this method has been called. + * + * @param indexes current positions of the instructions to be resized. Each + * instruction must be designated by the index of its <i>last</i> + * byte, plus one (or, in other words, by the index of the <i>first</i> + * byte of the <i>next</i> instruction). + * @param sizes the number of bytes to be <i>added</i> to the above + * instructions. More precisely, for each i < <tt>len</tt>, + * <tt>sizes</tt>[i] bytes will be added at the end of the + * instruction designated by <tt>indexes</tt>[i] or, if + * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| + * bytes of the instruction will be removed (the instruction size + * <i>must not</i> become negative or null). The gaps introduced by + * this method must be filled in "manually" in {@link #code code} + * method. + * @param len the number of instruction to be resized. Must be smaller than + * or equal to <tt>indexes</tt>.length and <tt>sizes</tt>.length. + * @return the <tt>indexes</tt> array, which now contains the new + * positions of the resized instructions (designated as above). + */ + private int[] resizeInstructions( + final int[] indexes, + final int[] sizes, + final int len) + { + 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[len]; // copy of indexes + int[] allSizes = new int[len]; // copy of sizes + boolean[] resize; // instructions to be resized + int newOffset; // future offset of a jump instruction + + System.arraycopy(indexes, 0, allIndexes, 0, len); + System.arraycopy(sizes, 0, allSizes, 0, len); + 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 <l> instruction with + // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx + // is the "opposite" opcode of IFxxx (i.e., + // IFNE for IFEQ) and where <l'> 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: + 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) { + for (i = allIndexes.length - 1; i >= 0; --i) { + if (allIndexes[i] == u) { + if (i < len) { + if (sizes[i] > 0) { + newCode.putByteArray(null, 0, sizes[i]); + } else { + newCode.length += sizes[i]; + } + indexes[i] = newCode.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 + // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is + // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) + // and where <l'> 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); + while (newCode.length % 4 != 0) { + newCode.putByte(0); + } + 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); + while (newCode.length % 4 != 0) { + newCode.putByte(0); + } + 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: + newCode.putByteArray(b, u, 5); + u += 5; + break; + // case MANA_INSN: + default: + newCode.putByteArray(b, u, 4); + u += 4; + break; + } + } + + // updates the exception handler block labels + Handler h = catchTable; + while (h != null) { + getNewOffset(allIndexes, allSizes, h.start); + getNewOffset(allIndexes, allSizes, h.end); + getNewOffset(allIndexes, allSizes, h.handler); + h = h.next; + } + 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 + while (cattrs != null) { + Label[] labels = cattrs.getLabels(); + if (labels != null) { + for (i = labels.length - 1; i >= 0; --i) { + if (!labels[i].resized) { + labels[i].position = getNewOffset(allIndexes, + allSizes, + 0, + labels[i].position); + labels[i].resized = true; + } + } + } + } + + // replaces old bytecodes with new ones + code = newCode; + + // returns the positions of the resized instructions + return indexes; + } + + /** + * 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. <p> Note: it is possible + * to have several entries for the same instruction in the <tt>indexes</tt> + * and <tt>sizes</tt>: 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 <i>last</i> + * byte, plus one (or, in other words, by the index of the <i>first</i> + * byte of the <i>next</i> instruction). + * @param sizes the number of bytes to be <i>added</i> to the above + * instructions. More precisely, for each i < <tt>len</tt>, + * <tt>sizes</tt>[i] bytes will be added at the end of the + * instruction designated by <tt>indexes</tt>[i] or, if + * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| + * bytes of the instruction will be removed (the instruction size + * <i>must not</i> 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 <i>last</i> + * byte, plus one (or, in other words, by the index of the <i>first</i> + * byte of the <i>next</i> instruction). + * @param sizes the number of bytes to be <i>added</i> to the above + * instructions. More precisely, for each i < <tt>len</tt>, + * <tt>sizes</tt>[i] bytes will be added at the end of the + * instruction designated by <tt>indexes</tt>[i] or, if + * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| + * bytes of the instruction will be removed (the instruction size + * <i>must not</i> 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.resized) { + label.position = getNewOffset(indexes, sizes, 0, label.position); + label.resized = true; + } + } +}
diff --git a/asmx/src/org/objectweb/asm/Opcodes.java b/asmx/src/org/objectweb/asm/Opcodes.java new file mode 100644 index 0000000..e5c2b33 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Opcodes.java
@@ -0,0 +1,361 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +/** + * Defines the JVM opcodes, access flags and array type codes. This interface + * does not define all the JVM opcodes because some opcodes are automatically + * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced + * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n + * opcodes are therefore not defined in this interface. Likewise for LDC, + * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and + * JSR_W. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public interface Opcodes { + + // ASM API versions + + int ASM4 = 4 << 16 | 0 << 8 | 0; + int ASM5 = 5 << 16 | 0 << 8 | 0; + + // versions + + int V1_1 = 3 << 16 | 45; + int V1_2 = 0 << 16 | 46; + int V1_3 = 0 << 16 | 47; + int V1_4 = 0 << 16 | 48; + int V1_5 = 0 << 16 | 49; + int V1_6 = 0 << 16 | 50; + int V1_7 = 0 << 16 | 51; + int V1_8 = 0 << 16 | 52; + + // access flags + + int ACC_PUBLIC = 0x0001; // class, field, method + int ACC_PRIVATE = 0x0002; // class, field, method + int ACC_PROTECTED = 0x0004; // class, field, method + int ACC_STATIC = 0x0008; // field, method + int ACC_FINAL = 0x0010; // class, field, method, parameter + int ACC_SUPER = 0x0020; // class + int ACC_SYNCHRONIZED = 0x0020; // method + int ACC_VOLATILE = 0x0040; // field + int ACC_BRIDGE = 0x0040; // method + int ACC_VARARGS = 0x0080; // method + int ACC_TRANSIENT = 0x0080; // field + int ACC_NATIVE = 0x0100; // method + int ACC_INTERFACE = 0x0200; // class + int ACC_ABSTRACT = 0x0400; // class, method + int ACC_STRICT = 0x0800; // method + int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter + int ACC_ANNOTATION = 0x2000; // class + int ACC_ENUM = 0x4000; // class(?) field inner + int ACC_MANDATED = 0x8000; // parameter + + // ASM specific pseudo access flags + + int ACC_DEPRECATED = 0x20000; // class, field, method + + // types for NEWARRAY + + int T_BOOLEAN = 4; + int T_CHAR = 5; + int T_FLOAT = 6; + int T_DOUBLE = 7; + int T_BYTE = 8; + int T_SHORT = 9; + int T_INT = 10; + int T_LONG = 11; + + // tags for Handle + + int H_GETFIELD = 1; + int H_GETSTATIC = 2; + int H_PUTFIELD = 3; + int H_PUTSTATIC = 4; + int H_INVOKEVIRTUAL = 5; + int H_INVOKESTATIC = 6; + int H_INVOKESPECIAL = 7; + int H_NEWINVOKESPECIAL = 8; + int H_INVOKEINTERFACE = 9; + + // stack map frame types + + /** + * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. + */ + int F_NEW = -1; + + /** + * Represents a compressed frame with complete frame data. + */ + int F_FULL = 0; + + /** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that additional 1-3 locals are defined, and + * with an empty stack. + */ + int F_APPEND = 1; + + /** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that the last 1-3 locals are absent and with + * an empty stack. + */ + int F_CHOP = 2; + + /** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with an empty stack. + */ + int F_SAME = 3; + + /** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with a single value on the stack. + */ + 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); + + // opcodes // visit method (- = idem) + + int NOP = 0; // visitInsn + int ACONST_NULL = 1; // - + int ICONST_M1 = 2; // - + int ICONST_0 = 3; // - + int ICONST_1 = 4; // - + int ICONST_2 = 5; // - + int ICONST_3 = 6; // - + int ICONST_4 = 7; // - + int ICONST_5 = 8; // - + int LCONST_0 = 9; // - + int LCONST_1 = 10; // - + int FCONST_0 = 11; // - + int FCONST_1 = 12; // - + int FCONST_2 = 13; // - + int DCONST_0 = 14; // - + int DCONST_1 = 15; // - + int BIPUSH = 16; // visitIntInsn + int SIPUSH = 17; // - + int LDC = 18; // visitLdcInsn + // int LDC_W = 19; // - + // int LDC2_W = 20; // - + int ILOAD = 21; // visitVarInsn + int LLOAD = 22; // - + int FLOAD = 23; // - + int DLOAD = 24; // - + int ALOAD = 25; // - + // int ILOAD_0 = 26; // - + // int ILOAD_1 = 27; // - + // int ILOAD_2 = 28; // - + // int ILOAD_3 = 29; // - + // int LLOAD_0 = 30; // - + // int LLOAD_1 = 31; // - + // int LLOAD_2 = 32; // - + // int LLOAD_3 = 33; // - + // int FLOAD_0 = 34; // - + // int FLOAD_1 = 35; // - + // int FLOAD_2 = 36; // - + // int FLOAD_3 = 37; // - + // int DLOAD_0 = 38; // - + // int DLOAD_1 = 39; // - + // int DLOAD_2 = 40; // - + // int DLOAD_3 = 41; // - + // int ALOAD_0 = 42; // - + // int ALOAD_1 = 43; // - + // int ALOAD_2 = 44; // - + // int ALOAD_3 = 45; // - + int IALOAD = 46; // visitInsn + int LALOAD = 47; // - + int FALOAD = 48; // - + int DALOAD = 49; // - + int AALOAD = 50; // - + int BALOAD = 51; // - + int CALOAD = 52; // - + int SALOAD = 53; // - + int ISTORE = 54; // visitVarInsn + int LSTORE = 55; // - + int FSTORE = 56; // - + int DSTORE = 57; // - + int ASTORE = 58; // - + // int ISTORE_0 = 59; // - + // int ISTORE_1 = 60; // - + // int ISTORE_2 = 61; // - + // int ISTORE_3 = 62; // - + // int LSTORE_0 = 63; // - + // int LSTORE_1 = 64; // - + // int LSTORE_2 = 65; // - + // int LSTORE_3 = 66; // - + // int FSTORE_0 = 67; // - + // int FSTORE_1 = 68; // - + // int FSTORE_2 = 69; // - + // int FSTORE_3 = 70; // - + // int DSTORE_0 = 71; // - + // int DSTORE_1 = 72; // - + // int DSTORE_2 = 73; // - + // int DSTORE_3 = 74; // - + // int ASTORE_0 = 75; // - + // int ASTORE_1 = 76; // - + // int ASTORE_2 = 77; // - + // int ASTORE_3 = 78; // - + int IASTORE = 79; // visitInsn + int LASTORE = 80; // - + int FASTORE = 81; // - + int DASTORE = 82; // - + int AASTORE = 83; // - + int BASTORE = 84; // - + int CASTORE = 85; // - + int SASTORE = 86; // - + int POP = 87; // - + int POP2 = 88; // - + int DUP = 89; // - + int DUP_X1 = 90; // - + int DUP_X2 = 91; // - + int DUP2 = 92; // - + int DUP2_X1 = 93; // - + int DUP2_X2 = 94; // - + int SWAP = 95; // - + int IADD = 96; // - + int LADD = 97; // - + int FADD = 98; // - + int DADD = 99; // - + int ISUB = 100; // - + int LSUB = 101; // - + int FSUB = 102; // - + int DSUB = 103; // - + int IMUL = 104; // - + int LMUL = 105; // - + int FMUL = 106; // - + int DMUL = 107; // - + int IDIV = 108; // - + int LDIV = 109; // - + int FDIV = 110; // - + int DDIV = 111; // - + int IREM = 112; // - + int LREM = 113; // - + int FREM = 114; // - + int DREM = 115; // - + int INEG = 116; // - + int LNEG = 117; // - + int FNEG = 118; // - + int DNEG = 119; // - + int ISHL = 120; // - + int LSHL = 121; // - + int ISHR = 122; // - + int LSHR = 123; // - + int IUSHR = 124; // - + int LUSHR = 125; // - + int IAND = 126; // - + int LAND = 127; // - + int IOR = 128; // - + int LOR = 129; // - + int IXOR = 130; // - + int LXOR = 131; // - + int IINC = 132; // visitIincInsn + int I2L = 133; // visitInsn + int I2F = 134; // - + int I2D = 135; // - + int L2I = 136; // - + int L2F = 137; // - + int L2D = 138; // - + int F2I = 139; // - + int F2L = 140; // - + int F2D = 141; // - + int D2I = 142; // - + int D2L = 143; // - + int D2F = 144; // - + int I2B = 145; // - + int I2C = 146; // - + int I2S = 147; // - + int LCMP = 148; // - + int FCMPL = 149; // - + int FCMPG = 150; // - + int DCMPL = 151; // - + int DCMPG = 152; // - + int IFEQ = 153; // visitJumpInsn + int IFNE = 154; // - + int IFLT = 155; // - + int IFGE = 156; // - + int IFGT = 157; // - + int IFLE = 158; // - + int IF_ICMPEQ = 159; // - + int IF_ICMPNE = 160; // - + int IF_ICMPLT = 161; // - + int IF_ICMPGE = 162; // - + int IF_ICMPGT = 163; // - + int IF_ICMPLE = 164; // - + int IF_ACMPEQ = 165; // - + int IF_ACMPNE = 166; // - + int GOTO = 167; // - + int JSR = 168; // - + int RET = 169; // visitVarInsn + int TABLESWITCH = 170; // visiTableSwitchInsn + int LOOKUPSWITCH = 171; // visitLookupSwitch + int IRETURN = 172; // visitInsn + int LRETURN = 173; // - + int FRETURN = 174; // - + int DRETURN = 175; // - + int ARETURN = 176; // - + int RETURN = 177; // - + int GETSTATIC = 178; // visitFieldInsn + int PUTSTATIC = 179; // - + int GETFIELD = 180; // - + int PUTFIELD = 181; // - + int INVOKEVIRTUAL = 182; // visitMethodInsn + int INVOKESPECIAL = 183; // - + int INVOKESTATIC = 184; // - + int INVOKEINTERFACE = 185; // - + int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn + int NEW = 187; // visitTypeInsn + int NEWARRAY = 188; // visitIntInsn + int ANEWARRAY = 189; // visitTypeInsn + int ARRAYLENGTH = 190; // visitInsn + int ATHROW = 191; // - + int CHECKCAST = 192; // visitTypeInsn + int INSTANCEOF = 193; // - + int MONITORENTER = 194; // visitInsn + int MONITOREXIT = 195; // - + // int WIDE = 196; // NOT VISITED + int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn + int IFNULL = 198; // visitJumpInsn + int IFNONNULL = 199; // - + // int GOTO_W = 200; // - + // int JSR_W = 201; // - +}
diff --git a/asmx/src/org/objectweb/asm/PrecompiledMethodVisitor.java b/asmx/src/org/objectweb/asm/PrecompiledMethodVisitor.java new file mode 100644 index 0000000..22407ca --- /dev/null +++ b/asmx/src/org/objectweb/asm/PrecompiledMethodVisitor.java
@@ -0,0 +1,18 @@ +package org.objectweb.asm; + +/** + * A visitor to visit a Java method in which the bytecode positions of the + * instructions and labels are already known. When an object that has these + * positions (for example, a {@link ClassReader}) accepts a + * {@link MethodVisitor}, it should test whether the visitor happens to be a + * {@link PrecompiledMethodVisitor}; if so, it should supply the positions + * using {@link #visitCurrentPosition}. + */ +public interface PrecompiledMethodVisitor extends MethodVisitor { + /** + * Informs the visitor of the current bytecode position in the method's + * code. This position applies to all labels and instructions visited + * until {@link #visitCurrentPosition} is called again. + */ + void visitCurrentPosition(int position); +}
diff --git a/asmx/src/org/objectweb/asm/Type.java b/asmx/src/org/objectweb/asm/Type.java new file mode 100644 index 0000000..33a8bf0 --- /dev/null +++ b/asmx/src/org/objectweb/asm/Type.java
@@ -0,0 +1,896 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2011 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * A Java field or method type. This class can be used to make it easier to + * manipulate type and method descriptors. + * + * @author Eric Bruneton + * @author Chris Nokleberg + */ +public class Type { + + /** + * The sort of the <tt>void</tt> type. See {@link #getSort getSort}. + */ + public static final int VOID = 0; + + /** + * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}. + */ + public static final int BOOLEAN = 1; + + /** + * The sort of the <tt>char</tt> type. See {@link #getSort getSort}. + */ + public static final int CHAR = 2; + + /** + * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}. + */ + public static final int BYTE = 3; + + /** + * The sort of the <tt>short</tt> type. See {@link #getSort getSort}. + */ + public static final int SHORT = 4; + + /** + * The sort of the <tt>int</tt> type. See {@link #getSort getSort}. + */ + public static final int INT = 5; + + /** + * The sort of the <tt>float</tt> type. See {@link #getSort getSort}. + */ + public static final int FLOAT = 6; + + /** + * The sort of the <tt>long</tt> type. See {@link #getSort getSort}. + */ + public static final int LONG = 7; + + /** + * The sort of the <tt>double</tt> type. See {@link #getSort getSort}. + */ + public static final int DOUBLE = 8; + + /** + * The sort of array reference types. See {@link #getSort getSort}. + */ + public static final int ARRAY = 9; + + /** + * The sort of object reference types. See {@link #getSort getSort}. + */ + public static final int OBJECT = 10; + + /** + * The sort of method types. See {@link #getSort getSort}. + */ + public static final int METHOD = 11; + + /** + * The <tt>void</tt> type. + */ + public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) + | (5 << 16) | (0 << 8) | 0, 1); + + /** + * The <tt>boolean</tt> type. + */ + public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) + | (0 << 16) | (5 << 8) | 1, 1); + + /** + * The <tt>char</tt> type. + */ + public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) + | (0 << 16) | (6 << 8) | 1, 1); + + /** + * The <tt>byte</tt> type. + */ + public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) + | (0 << 16) | (5 << 8) | 1, 1); + + /** + * The <tt>short</tt> type. + */ + public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) + | (0 << 16) | (7 << 8) | 1, 1); + + /** + * The <tt>int</tt> type. + */ + public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) + | (0 << 16) | (0 << 8) | 1, 1); + + /** + * The <tt>float</tt> type. + */ + public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) + | (2 << 16) | (2 << 8) | 1, 1); + + /** + * The <tt>long</tt> type. + */ + public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) + | (1 << 16) | (1 << 8) | 2, 1); + + /** + * The <tt>double</tt> type. + */ + public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) + | (3 << 16) | (3 << 8) | 2, 1); + + // ------------------------------------------------------------------------ + // Fields + // ------------------------------------------------------------------------ + + /** + * The sort of this Java type. + */ + private final int sort; + + /** + * A buffer containing the internal name of this Java type. This field is + * only used for reference types. + */ + private final char[] buf; + + /** + * The offset of the internal name of this Java type in {@link #buf buf} or, + * for primitive types, the size, descriptor and getOpcode offsets for this + * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset + * for IALOAD or IASTORE, byte 3 the offset for all other instructions). + */ + private final int off; + + /** + * The length of the internal name of this Java type. + */ + private final int len; + + // ------------------------------------------------------------------------ + // Constructors + // ------------------------------------------------------------------------ + + /** + * Constructs a reference type. + * + * @param sort + * the sort of the reference type to be constructed. + * @param buf + * a buffer containing the descriptor of the previous type. + * @param off + * the offset of this descriptor in the previous buffer. + * @param len + * the length of this descriptor. + */ + private Type(final int sort, final char[] buf, final int off, final int len) { + this.sort = sort; + this.buf = buf; + this.off = off; + this.len = len; + } + + /** + * Returns the Java type corresponding to the given type descriptor. + * + * @param typeDescriptor + * a field or method type descriptor. + * @return the Java type corresponding to the given type descriptor. + */ + public static Type getType(final String typeDescriptor) { + return getType(typeDescriptor.toCharArray(), 0); + } + + /** + * Returns the Java type corresponding to the given internal name. + * + * @param internalName + * an internal name. + * @return the Java type corresponding to the given internal name. + */ + public static Type getObjectType(final String internalName) { + char[] buf = internalName.toCharArray(); + return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length); + } + + /** + * Returns the Java type corresponding to the given method descriptor. + * Equivalent to <code>Type.getType(methodDescriptor)</code>. + * + * @param methodDescriptor + * a method descriptor. + * @return the Java type corresponding to the given method descriptor. + */ + public static Type getMethodType(final String methodDescriptor) { + return getType(methodDescriptor.toCharArray(), 0); + } + + /** + * Returns the Java method type corresponding to the given argument and + * return types. + * + * @param returnType + * the return type of the method. + * @param argumentTypes + * the argument types of the method. + * @return the Java type corresponding to the given argument and return + * types. + */ + public static Type getMethodType(final Type returnType, + final Type... argumentTypes) { + return getType(getMethodDescriptor(returnType, argumentTypes)); + } + + /** + * Returns the Java type corresponding to the given class. + * + * @param c + * a class. + * @return the Java type corresponding to the given class. + */ + public static Type getType(final Class<?> c) { + if (c.isPrimitive()) { + if (c == Integer.TYPE) { + return INT_TYPE; + } else if (c == Void.TYPE) { + return VOID_TYPE; + } else if (c == Boolean.TYPE) { + return BOOLEAN_TYPE; + } else if (c == Byte.TYPE) { + return BYTE_TYPE; + } else if (c == Character.TYPE) { + return CHAR_TYPE; + } else if (c == Short.TYPE) { + return SHORT_TYPE; + } else if (c == Double.TYPE) { + return DOUBLE_TYPE; + } else if (c == Float.TYPE) { + return FLOAT_TYPE; + } else /* if (c == Long.TYPE) */{ + return LONG_TYPE; + } + } else { + return getType(getDescriptor(c)); + } + } + + /** + * Returns the Java method type corresponding to the given constructor. + * + * @param c + * a {@link Constructor Constructor} object. + * @return the Java method type corresponding to the given constructor. + */ + public static Type getType(final Constructor<?> c) { + return getType(getConstructorDescriptor(c)); + } + + /** + * Returns the Java method type corresponding to the given method. + * + * @param m + * a {@link Method Method} object. + * @return the Java method type corresponding to the given method. + */ + public static Type getType(final Method m) { + return getType(getMethodDescriptor(m)); + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method descriptor. + * + * @param methodDescriptor + * a method descriptor. + * @return the Java types corresponding to the argument types of the given + * method descriptor. + */ + public static Type[] getArgumentTypes(final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + int off = 1; + int size = 0; + while (true) { + char car = buf[off++]; + if (car == ')') { + break; + } else if (car == 'L') { + while (buf[off++] != ';') { + } + ++size; + } else if (car != '[') { + ++size; + } + } + Type[] args = new Type[size]; + off = 1; + size = 0; + while (buf[off] != ')') { + args[size] = getType(buf, off); + off += args[size].len + (args[size].sort == OBJECT ? 2 : 0); + size += 1; + } + return args; + } + + /** + * Returns the Java types corresponding to the argument types of the given + * method. + * + * @param method + * a method. + * @return the Java types corresponding to the argument types of the given + * method. + */ + public static Type[] getArgumentTypes(final Method method) { + Class<?>[] classes = method.getParameterTypes(); + Type[] types = new Type[classes.length]; + for (int i = classes.length - 1; i >= 0; --i) { + types[i] = getType(classes[i]); + } + return types; + } + + /** + * Returns the Java type corresponding to the return type of the given + * method descriptor. + * + * @param methodDescriptor + * a method descriptor. + * @return the Java type corresponding to the return type of the given + * method descriptor. + */ + public static Type getReturnType(final String methodDescriptor) { + char[] buf = methodDescriptor.toCharArray(); + return getType(buf, methodDescriptor.indexOf(')') + 1); + } + + /** + * Returns the Java type corresponding to the return type of the given + * method. + * + * @param method + * a method. + * @return the Java type corresponding to the return type of the given + * method. + */ + public static Type getReturnType(final Method method) { + return getType(method.getReturnType()); + } + + /** + * Computes the size of the arguments and of the return value of a method. + * + * @param desc + * the descriptor of a 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 = + * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to + * <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). + */ + public static int getArgumentsAndReturnSizes(final String desc) { + int n = 1; + int c = 1; + while (true) { + char car = desc.charAt(c++); + if (car == ')') { + car = desc.charAt(c); + return n << 2 + | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); + } else if (car == 'L') { + while (desc.charAt(c++) != ';') { + } + n += 1; + } else if (car == '[') { + while ((car = desc.charAt(c)) == '[') { + ++c; + } + if (car == 'D' || car == 'J') { + n -= 1; + } + } else if (car == 'D' || car == 'J') { + n += 2; + } else { + n += 1; + } + } + } + + /** + * Returns the Java type corresponding to the given type descriptor. For + * method descriptors, buf is supposed to contain nothing more than the + * descriptor itself. + * + * @param buf + * a buffer containing a type descriptor. + * @param off + * the offset of this descriptor in the previous buffer. + * @return the Java type corresponding to the given type descriptor. + */ + private static Type getType(final char[] buf, final int off) { + int len; + switch (buf[off]) { + case 'V': + return VOID_TYPE; + case 'Z': + return BOOLEAN_TYPE; + case 'C': + return CHAR_TYPE; + case 'B': + return BYTE_TYPE; + case 'S': + return SHORT_TYPE; + case 'I': + return INT_TYPE; + case 'F': + return FLOAT_TYPE; + case 'J': + return LONG_TYPE; + case 'D': + return DOUBLE_TYPE; + case '[': + len = 1; + while (buf[off + len] == '[') { + ++len; + } + if (buf[off + len] == 'L') { + ++len; + while (buf[off + len] != ';') { + ++len; + } + } + return new Type(ARRAY, buf, off, len + 1); + case 'L': + len = 1; + while (buf[off + len] != ';') { + ++len; + } + return new Type(OBJECT, buf, off + 1, len - 1); + // case '(': + default: + return new Type(METHOD, buf, off, buf.length - off); + } + } + + // ------------------------------------------------------------------------ + // Accessors + // ------------------------------------------------------------------------ + + /** + * Returns the sort of this Java type. + * + * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR}, + * {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT}, + * {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE}, + * {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD + * METHOD}. + */ + public int getSort() { + return sort; + } + + /** + * Returns the number of dimensions of this array type. This method should + * only be used for an array type. + * + * @return the number of dimensions of this array type. + */ + public int getDimensions() { + int i = 1; + while (buf[off + i] == '[') { + ++i; + } + return i; + } + + /** + * Returns the type of the elements of this array type. This method should + * only be used for an array type. + * + * @return Returns the type of the elements of this array type. + */ + public Type getElementType() { + return getType(buf, off + getDimensions()); + } + + /** + * Returns the binary name of the class corresponding to this type. This + * method must not be used on method types. + * + * @return the binary name of the class corresponding to this type. + */ + public String getClassName() { + switch (sort) { + case VOID: + return "void"; + case BOOLEAN: + return "boolean"; + case CHAR: + return "char"; + case BYTE: + return "byte"; + case SHORT: + return "short"; + case INT: + return "int"; + case FLOAT: + return "float"; + case LONG: + return "long"; + case DOUBLE: + return "double"; + case ARRAY: + StringBuilder sb = new StringBuilder(getElementType().getClassName()); + for (int i = getDimensions(); i > 0; --i) { + sb.append("[]"); + } + return sb.toString(); + case OBJECT: + return new String(buf, off, len).replace('/', '.'); + default: + return null; + } + } + + /** + * Returns the internal name of the class corresponding to this object or + * array type. The internal name of a class is its fully qualified name (as + * returned by Class.getName(), where '.' are replaced by '/'. This method + * should only be used for an object or array type. + * + * @return the internal name of the class corresponding to this object type. + */ + public String getInternalName() { + return new String(buf, off, len); + } + + /** + * Returns the argument types of methods of this type. This method should + * only be used for method types. + * + * @return the argument types of methods of this type. + */ + public Type[] getArgumentTypes() { + return getArgumentTypes(getDescriptor()); + } + + /** + * Returns the return type of methods of this type. This method should only + * be used for method types. + * + * @return the return type of methods of this type. + */ + public Type getReturnType() { + return getReturnType(getDescriptor()); + } + + /** + * Returns the size of the arguments and of the return value of methods of + * this type. This method should only be used for method types. + * + * @return the size of the arguments (plus one for the implicit this + * argument), argSize, and the size of the return value, retSize, + * packed into a single + * int i = <tt>(argSize << 2) | retSize</tt> + * (argSize is therefore equal to <tt>i >> 2</tt>, + * and retSize to <tt>i & 0x03</tt>). + */ + public int getArgumentsAndReturnSizes() { + return getArgumentsAndReturnSizes(getDescriptor()); + } + + // ------------------------------------------------------------------------ + // Conversion to type descriptors + // ------------------------------------------------------------------------ + + /** + * Returns the descriptor corresponding to this Java type. + * + * @return the descriptor corresponding to this Java type. + */ + public String getDescriptor() { + StringBuffer buf = new StringBuffer(); + getDescriptor(buf); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given argument and return + * types. + * + * @param returnType + * the return type of the method. + * @param argumentTypes + * the argument types of the method. + * @return the descriptor corresponding to the given argument and return + * types. + */ + public static String getMethodDescriptor(final Type returnType, + final Type... argumentTypes) { + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < argumentTypes.length; ++i) { + argumentTypes[i].getDescriptor(buf); + } + buf.append(')'); + returnType.getDescriptor(buf); + return buf.toString(); + } + + /** + * Appends the descriptor corresponding to this Java type to the given + * string buffer. + * + * @param buf + * the string buffer to which the descriptor must be appended. + */ + private void getDescriptor(final StringBuffer buf) { + if (this.buf == null) { + // descriptor is in byte 3 of 'off' for primitive types (buf == + // null) + buf.append((char) ((off & 0xFF000000) >>> 24)); + } else if (sort == OBJECT) { + buf.append('L'); + buf.append(this.buf, off, len); + buf.append(';'); + } else { // sort == ARRAY || sort == METHOD + buf.append(this.buf, off, len); + } + } + + // ------------------------------------------------------------------------ + // Direct conversion from classes to type descriptors, + // without intermediate Type objects + // ------------------------------------------------------------------------ + + /** + * Returns the internal name of the given class. The internal name of a + * class is its fully qualified name, as returned by Class.getName(), where + * '.' are replaced by '/'. + * + * @param c + * an object or array class. + * @return the internal name of the given class. + */ + public static String getInternalName(final Class<?> c) { + return c.getName().replace('.', '/'); + } + + /** + * Returns the descriptor corresponding to the given Java type. + * + * @param c + * an object class, a primitive class or an array class. + * @return the descriptor corresponding to the given class. + */ + public static String getDescriptor(final Class<?> c) { + StringBuffer buf = new StringBuffer(); + getDescriptor(buf, c); + return buf.toString(); + } + + /** + * Returns the descriptor corresponding to the given constructor. + * + * @param c + * a {@link Constructor Constructor} object. + * @return the descriptor of the given constructor. + */ + public static String getConstructorDescriptor(final Constructor<?> c) { + Class<?>[] parameters = c.getParameterTypes(); + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < parameters.length; ++i) { + getDescriptor(buf, parameters[i]); + } + return buf.append(")V").toString(); + } + + /** + * Returns the descriptor corresponding to the given method. + * + * @param m + * a {@link Method Method} object. + * @return the descriptor of the given method. + */ + public static String getMethodDescriptor(final Method m) { + Class<?>[] parameters = m.getParameterTypes(); + StringBuffer buf = new StringBuffer(); + buf.append('('); + for (int i = 0; i < parameters.length; ++i) { + getDescriptor(buf, parameters[i]); + } + buf.append(')'); + getDescriptor(buf, m.getReturnType()); + return buf.toString(); + } + + /** + * Appends the descriptor of the given class to the given string buffer. + * + * @param buf + * the string buffer to which the descriptor must be appended. + * @param c + * the class whose descriptor must be computed. + */ + private static void getDescriptor(final StringBuffer buf, final Class<?> c) { + Class<?> d = c; + while (true) { + if (d.isPrimitive()) { + char car; + if (d == Integer.TYPE) { + car = 'I'; + } else if (d == Void.TYPE) { + car = 'V'; + } else if (d == Boolean.TYPE) { + car = 'Z'; + } else if (d == Byte.TYPE) { + car = 'B'; + } else if (d == Character.TYPE) { + car = 'C'; + } else if (d == Short.TYPE) { + car = 'S'; + } else if (d == Double.TYPE) { + car = 'D'; + } else if (d == Float.TYPE) { + car = 'F'; + } else /* if (d == Long.TYPE) */{ + car = 'J'; + } + buf.append(car); + return; + } else if (d.isArray()) { + buf.append('['); + d = d.getComponentType(); + } else { + buf.append('L'); + String name = d.getName(); + int len = name.length(); + for (int i = 0; i < len; ++i) { + char car = name.charAt(i); + buf.append(car == '.' ? '/' : car); + } + buf.append(';'); + return; + } + } + } + + // ------------------------------------------------------------------------ + // Corresponding size and opcodes + // ------------------------------------------------------------------------ + + /** + * Returns the size of values of this type. This method must not be used for + * method types. + * + * @return the size of values of this type, i.e., 2 for <tt>long</tt> and + * <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise. + */ + public int getSize() { + // the size is in byte 0 of 'off' for primitive types (buf == null) + return buf == null ? (off & 0xFF) : 1; + } + + /** + * Returns a JVM instruction opcode adapted to this Java type. This method + * must not be used for method types. + * + * @param opcode + * a JVM instruction opcode. This opcode must be one of ILOAD, + * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, + * ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. + * @return an opcode that is similar to the given opcode, but adapted to + * this Java type. For example, if this type is <tt>float</tt> and + * <tt>opcode</tt> is IRETURN, this method returns FRETURN. + */ + public int getOpcode(final int opcode) { + if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { + // the offset for IALOAD or IASTORE is in byte 1 of 'off' for + // primitive types (buf == null) + return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4); + } else { + // the offset for other instructions is in byte 2 of 'off' for + // primitive types (buf == null) + return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4); + } + } + + // ------------------------------------------------------------------------ + // Equals, hashCode and toString + // ------------------------------------------------------------------------ + + /** + * Tests if the given object is equal to this type. + * + * @param o + * the object to be compared to this type. + * @return <tt>true</tt> if the given object is equal to this type. + */ + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Type)) { + return false; + } + Type t = (Type) o; + if (sort != t.sort) { + return false; + } + if (sort >= ARRAY) { + if (len != t.len) { + return false; + } + for (int i = off, j = t.off, end = i + len; i < end; i++, j++) { + if (buf[i] != t.buf[j]) { + return false; + } + } + } + return true; + } + + /** + * Returns a hash code value for this type. + * + * @return a hash code value for this type. + */ + @Override + public int hashCode() { + int hc = 13 * sort; + if (sort >= ARRAY) { + for (int i = off, end = i + len; i < end; i++) { + hc = 17 * (hc + buf[i]); + } + } + return hc; + } + + /** + * Returns a string representation of this type. + * + * @return the descriptor of this type. + */ + @Override + public String toString() { + return getDescriptor(); + } +}
diff --git a/asmx/src/org/objectweb/asm/TypeAnnotationVisitor.java b/asmx/src/org/objectweb/asm/TypeAnnotationVisitor.java new file mode 100644 index 0000000..ec93575 --- /dev/null +++ b/asmx/src/org/objectweb/asm/TypeAnnotationVisitor.java
@@ -0,0 +1,119 @@ +package org.objectweb.asm; + +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +/** + * A visitor to visit a Java extended annotation. The methods of this + * interface must be called in the following order: + * (<tt>visit<tt> | + * <tt>visitEnum<tt> | + * <tt>visitAnnotation<tt> | + * <tt>visitArray<tt>)* + * + * <tt>visitXTargetType<tt> + * + * (<i>visit the fields (if any) of the <tt>reference_info</tt> structure + * for the given target type in the correct order using <tt>visitXOffset</tt>, + * <tt>visitXStartPc</tt>, <tt>visitXLength</tt>, <tt>visitXIndex</tt>, + * <tt>visitXParamIndex</tt>, <tt>visitXBoundIndex</tt>, + * <tt>visitXLocationLength</tt>, and/or <tt>visitXLocation</tt></i>) + * + * <tt>visitEnd<tt> + * + * @author jaimeq + */ + +public interface TypeAnnotationVisitor extends AnnotationVisitor { + /** + * Visits the target type of the extended annotation, which defines the + * type and structure of the reference info of the extended annotation. + * + * @param target_type the target type of the extended annotation + */ + void visitXTargetType(int target_type); + + /** + * Visits the offset specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * + * @param offset the offset specified by the extended annotation + */ + void visitXOffset(int offset); + + /** + * Visits the location_length specified by the extended annotation, whose + * meaning depends on the extended annotation's target type. + * + * @param location_length the location_length specified by the extended + * annotation + */ + void visitXLocationLength(int location_length); + + /** + * Visits the location specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * + * @param location the location specified by the extended annotation + */ + void visitXLocation(TypePathEntry location); + + void visitXNumEntries(int num_entries); + + /** + * Visits the start_pc specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * + * @param start_pc the start_pc specified by the extended annotation + */ + void visitXStartPc(int start_pc); + + /** + * Visits the length specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * + * @param length the length specified by the extended annotation + */ + void visitXLength(int length); + + /** + * Visits the index specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * + * @param index the index specified by the extended annotation + */ + void visitXIndex(int index); + + /** + * Visits the param_index specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * + * @param param_index the param_index specified by the extended annotation + */ + public void visitXParamIndex(int param_index); + + /** + * Visits the bound_index specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * + * @param bound_index the bound_index specified by the extended annotation + */ + public void visitXBoundIndex(int bound_index); + + /** + * Visits the type_index specified by the extended annotation, whose meaning + * depends on the extended annotation's target type. + * @param type_index + */ + public void visitXTypeIndex(int type_index); + + /** + * Visits the exception_index specified by the extended annotation. + * @param exception_index + */ + public void visitXExceptionIndex(int exception_index); + + /** + * Visits the annotation name and arguments size. + */ + public void visitXNameAndArgsSize(); +}
diff --git a/asmx/src/org/objectweb/asm/TypeAnnotationWriter.java b/asmx/src/org/objectweb/asm/TypeAnnotationWriter.java new file mode 100644 index 0000000..fdff53f --- /dev/null +++ b/asmx/src/org/objectweb/asm/TypeAnnotationWriter.java
@@ -0,0 +1,405 @@ +// Source code copied from AnnotationWriter.java, and modified to +// accommodate extended annotations. +// Specifically, the int x* fields and visitX* methods were added. + +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +/** + * An {@link TypeAnnotationVisitor} that generates + * extended annotations in bytecode form. + * + * @author jaimeq + */ +final class TypeAnnotationWriter implements TypeAnnotationVisitor { + + /** + * The class writer to which this annotation must be added. + */ + private final ClassWriter cw; + + /** + * The number of values in this annotation. + */ + private int size; + + /** + * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation + * writers used for annotation default and annotation arrays use unnamed + * values. + */ + private final boolean named; + + /** + * The annotation values in bytecode form. This byte vector only contains + * the values themselves, i.e. the number of values must be stored as a + * unsigned short just before these bytes. + */ + private final ByteVector bv; + + /** + * The byte vector to be used to store the number of values of this + * annotation. See {@link #bv}. + */ + private final ByteVector parent; + + /** + * Where the number of values of this annotation must be stored in + * {@link #parent}. + */ + private int offset; + + /** + * The name of this annotation. + */ + private final String desc; + + /** + * Next annotation writer. This field is used to store annotation lists. + */ + TypeAnnotationWriter next; + + /** + * Previous annotation writer. This field is used to store annotation lists. + */ + TypeAnnotationWriter prev; + + private TypePathEntry xlocations[]; + private int xlocations_index; + + // ------------------------------------------------------------------------ + // Constructor + // ------------------------------------------------------------------------ + + /** + * Constructs a new {@link TypeAnnotationWriter}. + * + * @param cw the class writer to which this annotation must be added. + * @param named <tt>true<tt> if values are named, <tt>false</tt> otherwise. + * @param bv where the annotation values must be stored. + * @param parent where the number of annotation values must be stored. + * @param desc the name of this annotation. + */ + TypeAnnotationWriter( + final ClassWriter cw, + final boolean named, + final ByteVector bv, + final ByteVector parent, + final String desc) + { + this.cw = cw; + this.named = named; + this.bv = bv; + this.parent = parent; + this.desc = desc; + + // extended information + this.xlocations = null; + this.xlocations_index = 0; + } + + // ------------------------------------------------------------------------ + // Implementation of the AnnotationVisitor interface + // ------------------------------------------------------------------------ + + public void visit(final String name, final Object value) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + if (value instanceof String) { + bv.put12('s', cw.newUTF8((String) value)); + } else if (value instanceof Byte) { + bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); + } else if (value instanceof Boolean) { + int v = ((Boolean) value).booleanValue() ? 1 : 0; + bv.put12('Z', cw.newInteger(v).index); + } else if (value instanceof Character) { + bv.put12('C', cw.newInteger(((Character) value).charValue()).index); + } else if (value instanceof Short) { + bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); + } else if (value instanceof Type) { + bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); + } else if (value instanceof byte[]) { + byte[] v = (byte[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('B', cw.newInteger(v[i]).index); + } + } else if (value instanceof boolean[]) { + boolean[] v = (boolean[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); + } + } else if (value instanceof short[]) { + short[] v = (short[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('S', cw.newInteger(v[i]).index); + } + } else if (value instanceof char[]) { + char[] v = (char[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('C', cw.newInteger(v[i]).index); + } + } else if (value instanceof int[]) { + int[] v = (int[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('I', cw.newInteger(v[i]).index); + } + } else if (value instanceof long[]) { + long[] v = (long[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('J', cw.newLong(v[i]).index); + } + } else if (value instanceof float[]) { + float[] v = (float[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('F', cw.newFloat(v[i]).index); + } + } else if (value instanceof double[]) { + double[] v = (double[]) value; + bv.put12('[', v.length); + for (int i = 0; i < v.length; i++) { + bv.put12('D', cw.newDouble(v[i]).index); + } + } else { + Item i = cw.newConstItem(value); + bv.put12(".s.IFJDCS".charAt(i.type), i.index); + } + } + + public void visitEnum( + final String name, + final String desc, + final String value) + { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); + } + + public AnnotationVisitor visitAnnotation( + final String name, + final String desc) + { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + // write tag and type, and reserve space for values count + bv.put12('@', cw.newUTF8(desc)).putShort(0); + return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); + } + + public AnnotationVisitor visitArray(final String name) { + ++size; + if (named) { + bv.putShort(cw.newUTF8(name)); + } + // write tag, and reserve space for array size + bv.put12('[', 0); + return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); + } + + public void visitEnd() { + if (parent != null) { + byte[] data = parent.data; + data[offset] = (byte) (size >>> 8); + data[offset + 1] = (byte) size; + } + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Returns the size of this annotation writer list. + * + * @return the size of this annotation writer list. + */ + int getSize() { + int size = 0; + TypeAnnotationWriter aw = this; + while (aw != null) { + size += aw.bv.length; + aw = aw.next; + } + return size; + } + + /** + * Puts the annotations of this annotation writer list into the given byte + * vector. + * + * @param out where the annotations must be put. + */ + void put(final ByteVector out) { + int n = 0; + int size = 2; + TypeAnnotationWriter aw = this; + TypeAnnotationWriter last = null; + while (aw != null) { + ++n; + size += aw.bv.length; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putInt(size); + out.putShort(n); + aw = last; + while (aw != null) { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } + } + + /** + * Puts the given annotation lists into the given byte vector. + * + * @param panns an array of annotation writer lists. + * @param out where the annotations must be put. + */ + static void put(final TypeAnnotationWriter[] panns, final ByteVector out) { + int size = 1 + 2 * panns.length; + for (int i = 0; i < panns.length; ++i) { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + out.putInt(size).putByte(panns.length); + for (int i = 0; i < panns.length; ++i) { + TypeAnnotationWriter aw = panns[i]; + TypeAnnotationWriter last = null; + int n = 0; + while (aw != null) { + ++n; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putShort(n); + aw = last; + while (aw != null) { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } + } + } + + // ------------------------------------------------------------------------ + // Implementation of the TypeAnnotationVisitor interface + // ------------------------------------------------------------------------ + + // below are all the methods for implementing extended annotations + public void visitXTargetType(int target_type) { + bv.putByte(target_type); + } + + // used for typecasts, object creation, field generic/array + public void visitXOffset(int offset) { + bv.putShort(offset); + } + + // used for generic type arguments or arrays + public void visitXLocationLength(int location_length) { + this.xlocations = new TypePathEntry[location_length]; + this.xlocations_index = 0; + bv.putByte(location_length); + } + + // used for generic type arguments or arrays + public void visitXLocation(TypePathEntry location) { + this.xlocations[this.xlocations_index] = location; + this.xlocations_index++; + bv.putByte(location.tag.tag); + bv.putByte(location.arg); + } + + // used for local variables + public void visitXNumEntries(int num_entries) { + bv.putShort(num_entries); + } + + // used for local variables + public void visitXStartPc(int start_pc) { + bv.putShort(start_pc); + } + + // used for local variables + public void visitXLength(int length) { + bv.putShort(length); + } + + // used for local variables + public void visitXIndex(int index) { + bv.putShort(index); + } + + // used for type parameter bounds + public void visitXParamIndex(int param_index) { + bv.putByte(param_index); + } + + // used for type parameter bounds + public void visitXBoundIndex(int bound_index) { + bv.putByte(bound_index); + } + + // used for type index for class extends/implements and + // throws exception types + public void visitXTypeIndex(int type_index) { + bv.putByte(type_index); + } + + public void visitXExceptionIndex(int exception_index) { + bv.putByte(exception_index); + } + + public void visitXNameAndArgsSize() { + bv.putShort(cw.newUTF8(desc)); + // Placeholder for size + bv.putShort(0); + // Set offset so we know where to put the size on a call to visitEnd + offset = bv.length - 2; + } +}
diff --git a/asmx/src/org/objectweb/asm/TypePath.java b/asmx/src/org/objectweb/asm/TypePath.java new file mode 100644 index 0000000..d9c99b1 --- /dev/null +++ b/asmx/src/org/objectweb/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 org.objectweb.asm; + +/** + * The path to a type argument, wildcard bound, array element type, or static + * inner type within an enclosing type. + * + * @author Eric Bruneton + */ +public 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/asmx/src/org/objectweb/asm/attrs/StackMapAttribute.java b/asmx/src/org/objectweb/asm/attrs/StackMapAttribute.java new file mode 100644 index 0000000..4df2f7d --- /dev/null +++ b/asmx/src/org/objectweb/asm/attrs/StackMapAttribute.java
@@ -0,0 +1,378 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.attrs; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; + +/** + * StackMapAttribute is used by CDLC preverifier. Definition is given in + * appendix "CLDC Byte Code Typechecker Specification" from CDLC 1.1 + * specification. <p> <i>Note that this implementation does not calculate + * StackMapFrame structures from the method bytecode. If method code is changed + * or generated from scratch, then developer is responsible to prepare a correct + * StackMapFrame structures.</i> <p> The format of the stack map in the class + * file is given below. In the following, <ul> <li>if the length of the + * method's byte code1 is 65535 or less, then <tt>uoffset</tt> represents the + * type u2; otherwise <tt>uoffset</tt> represents the type u4.</li> <li>If + * the maximum number of local variables for the method is 65535 or less, then + * <tt>ulocalvar</tt> represents the type u2; otherwise <tt>ulocalvar</tt> + * represents the type u4.</li> <li>If the maximum size of the operand stack + * is 65535 or less, then <tt>ustack</tt> represents the type u2; otherwise + * ustack represents the type u4.</li> </ul> + * + * <pre> + * stack_map { // attribute StackMap + * u2 attribute_name_index; + * u4 attribute_length + * uoffset number_of_entries; + * stack_map_frame entries[number_of_entries]; + * } + * </pre> + * + * Each stack map frame has the following format: + * + * <pre> + * stack_map_frame { + * uoffset offset; + * ulocalvar number_of_locals; + * verification_type_info locals[number_of_locals]; + * ustack number_of_stack_items; + * verification_type_info stack[number_of_stack_items]; + * } + * </pre> + * + * The <tt>verification_type_info</tt> structure consists of a one-byte tag + * followed by zero or more bytes, giving more information about the tag. Each + * <tt>verification_type_info</tt> structure specifies the verification type + * of one or two locations. + * + * <pre> + * union verification_type_info { + * Top_variable_info; + * Integer_variable_info; + * Float_variable_info; + * Long_variable_info; + * Double_variable_info; + * Null_variable_info; + * UninitializedThis_variable_info; + * Object_variable_info; + * Uninitialized_variable_info; + * } + * + * Top_variable_info { + * u1 tag = ITEM_Top; // 0 + * } + * + * Integer_variable_info { + * u1 tag = ITEM_Integer; // 1 + * } + * + * Float_variable_info { + * u1 tag = ITEM_Float; // 2 + * } + * + * Long_variable_info { + * u1 tag = ITEM_Long; // 4 + * } + * + * Double_variable_info { + * u1 tag = ITEM_Double; // 3 + * } + * + * Null_variable_info { + * u1 tag = ITEM_Null; // 5 + * } + * + * UninitializedThis_variable_info { + * u1 tag = ITEM_UninitializedThis; // 6 + * } + * + * Object_variable_info { + * u1 tag = ITEM_Object; // 7 + * u2 cpool_index; + * } + * + * Uninitialized_variable_info { + * u1 tag = ITEM_Uninitialized // 8 + * uoffset offset; + * } + * </pre> + * + * @see <a href="http://www.jcp.org/en/jsr/detail?id=139">JSR 139 : Connected + * Limited Device Configuration 1.1</a> + * + * @author Eugene Kuleshov + */ +public class StackMapAttribute extends Attribute { + + static final int MAX_SIZE = 65535; + + /** + * A List of <code>StackMapFrame</code> instances. + */ + public List frames = new ArrayList(); + + public StackMapAttribute() { + super("StackMap"); + } + + public StackMapAttribute(List frames) { + this(); + this.frames = frames; + } + + public List getFrames() { + return frames; + } + + public StackMapFrame getFrame(Label label) { + for (int i = 0; i < frames.size(); i++) { + StackMapFrame frame = (StackMapFrame) frames.get(i); + if (frame.label == label) { + return frame; + } + } + return null; + } + + public boolean isUnknown() { + return false; + } + + public boolean isCodeAttribute() { + return true; + } + + protected Attribute read( + ClassReader cr, + int off, + int len, + char[] buf, + int codeOff, + Label[] labels) + { + StackMapAttribute attr = new StackMapAttribute(); + // note that this is not the size of Code attribute + boolean isExtCodeSize = cr.readInt(codeOff + 4) > MAX_SIZE; + boolean isExtLocals = cr.readUnsignedShort(codeOff + 2) > MAX_SIZE; + boolean isExtStack = cr.readUnsignedShort(codeOff) > MAX_SIZE; + + int size = 0; + if (isExtCodeSize) { + size = cr.readInt(off); + off += 4; + } else { + size = cr.readUnsignedShort(off); + off += 2; + } + for (int i = 0; i < size; i++) { + int offset; + if (isExtCodeSize) { + offset = cr.readInt(off); + off += 4; + } else { + offset = cr.readUnsignedShort(off); + off += 2; + } + + Label label = getLabel(offset, labels); + List locals = new ArrayList(); + List stack = new ArrayList(); + + off = readTypeInfo(cr, + off, + locals, + labels, + buf, + isExtLocals, + isExtCodeSize); + off = readTypeInfo(cr, + off, + stack, + labels, + buf, + isExtStack, + isExtCodeSize); + + attr.frames.add(new StackMapFrame(label, locals, stack)); + } + return attr; + } + + private int readTypeInfo( + ClassReader cr, + int off, + List info, + Label[] labels, + char[] buf, + boolean isExt, + boolean isExtCode) + { + int n = 0; + if (isExt) { + n = cr.readInt(off); + off += 4; + } else { + n = cr.readUnsignedShort(off); + off += 2; + } + for (int j = 0; j < n; j++) { + int itemType = cr.readByte(off++); + StackMapType typeInfo = StackMapType.getTypeInfo(itemType); + info.add(typeInfo); + switch (itemType) { + case StackMapType.ITEM_Object: // + typeInfo.setObject(cr.readClass(off, buf)); + off += 2; + break; + case StackMapType.ITEM_Uninitialized: // + int offset; + if (isExtCode) { + offset = cr.readInt(off); + off += 4; + } else { + offset = cr.readUnsignedShort(off); + off += 2; + } + typeInfo.setLabel(getLabel(offset, labels)); + break; + } + } + return off; + } + + private void writeTypeInfo(ByteVector bv, ClassWriter cw, List info, int max) + { + if (max > StackMapAttribute.MAX_SIZE) { + bv.putInt(info.size()); + } else { + bv.putShort(info.size()); + } + for (int j = 0; j < info.size(); j++) { + StackMapType typeInfo = (StackMapType) info.get(j); + bv.putByte(typeInfo.getType()); + switch (typeInfo.getType()) { + case StackMapType.ITEM_Object: // + bv.putShort(cw.newClass(typeInfo.getObject())); + break; + + case StackMapType.ITEM_Uninitialized: // + bv.putShort(typeInfo.getLabel().getOffset()); + break; + + } + } + } + + private Label getLabel(int offset, Label[] labels) { + Label l = labels[offset]; + if (l != null) { + return l; + } + return labels[offset] = new Label(); + } + + protected ByteVector write( + ClassWriter cw, + byte[] code, + int len, + int maxStack, + int maxLocals) + { + ByteVector bv = new ByteVector(); + if (code != null && code.length > MAX_SIZE) { // TODO verify value + bv.putInt(frames.size()); + } else { + bv.putShort(frames.size()); + } + for (int i = 0; i < frames.size(); i++) { + writeFrame((StackMapFrame) frames.get(i), + cw, + maxStack, + maxLocals, + bv); + } + return bv; + } + + protected Label[] getLabels() { + HashSet labels = new HashSet(); + for (int i = 0; i < frames.size(); i++) { + getFrameLabels((StackMapFrame) frames.get(i), labels); + } + return (Label[]) labels.toArray(new Label[labels.size()]); + } + + private void writeFrame( + StackMapFrame frame, + ClassWriter cw, + int maxStack, + int maxLocals, + ByteVector bv) + { + bv.putShort(frame.label.getOffset()); + writeTypeInfo(bv, cw, frame.locals, maxLocals); + writeTypeInfo(bv, cw, frame.stack, maxStack); + } + + private void getFrameLabels(StackMapFrame frame, Set labels) { + labels.add(frame.label); + getTypeInfoLabels(labels, frame.locals); + getTypeInfoLabels(labels, frame.stack); + } + + private void getTypeInfoLabels(Set labels, List info) { + for (Iterator it = info.iterator(); it.hasNext();) { + StackMapType typeInfo = (StackMapType) it.next(); + if (typeInfo.getType() == StackMapType.ITEM_Uninitialized) { + labels.add(typeInfo.getLabel()); + } + } + } + + public String toString() { + StringBuffer sb = new StringBuffer("StackMap["); + for (int i = 0; i < frames.size(); i++) { + sb.append('\n').append('[').append(frames.get(i)).append(']'); + } + sb.append("\n]"); + return sb.toString(); + } +}
diff --git a/asmx/src/org/objectweb/asm/attrs/StackMapFrame.java b/asmx/src/org/objectweb/asm/attrs/StackMapFrame.java new file mode 100644 index 0000000..bdd4641 --- /dev/null +++ b/asmx/src/org/objectweb/asm/attrs/StackMapFrame.java
@@ -0,0 +1,82 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.attrs; + +import java.util.List; + +import org.objectweb.asm.Label; + +/** + * Holds the state of the stack and local variables for a single execution + * branch. + * + * <i>Note that Long and Double types are represented by two entries in locals + * and stack. Second entry should be always of type Top.</i> + * + * @see <a href="http://www.jcp.org/en/jsr/detail?id=139">JSR 139 : Connected + * Limited Device Configuration 1.1</a> + * + * @see "ClassFileFormat-Java6.fm Page 138 Friday, April 15, 2005 3:22 PM" + * + * @author Eugene Kuleshov + */ +public class StackMapFrame { + + /** + * A <code>Label</code> for frame offset within method bytecode. + */ + public Label label; + + /** + * A List of <code>StackMapType</code> instances that represent locals for + * this frame. + */ + public List locals; + + /** + * A List of <code>StackMapType</code> instances that represent stack for + * this frame. + */ + public List stack; + + public StackMapFrame(Label label, List locals, List stack) { + this.label = label; + this.locals = locals; + this.stack = stack; + } + + public String toString() { + StringBuffer sb = new StringBuffer("Frame:L"); + sb.append(System.identityHashCode(label)); + sb.append(" locals").append(locals); + sb.append(" stack").append(stack); + return sb.toString(); + } +}
diff --git a/asmx/src/org/objectweb/asm/attrs/StackMapTableAttribute.java b/asmx/src/org/objectweb/asm/attrs/StackMapTableAttribute.java new file mode 100644 index 0000000..d388104 --- /dev/null +++ b/asmx/src/org/objectweb/asm/attrs/StackMapTableAttribute.java
@@ -0,0 +1,927 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.attrs; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ByteVector; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * The stack map attribute is used during the process of verification by + * typechecking (§4.11.1). <br> <br> A stack map attribute consists of zero or + * more stack map frames. Each stack map frame specifies (either explicitly or + * implicitly) a bytecode offset, the verification types (§4.11.1) for the local + * variables, and the verification types for the operand stack. <br> <br> The + * type checker deals with and manipulates the expected types of a method's + * local variables and operand stack. Throughout this section, a location refers + * to either a single local variable or to a single operand stack entry. <br> + * <br> We will use the terms stack frame map and type state interchangeably to + * describe a mapping from locations in the operand stack and local variables of + * a method to verification types. We will usually use the term stack frame map + * when such a mapping is provided in the class file, and the term type state + * when the mapping is inferred by the type checker. <br> <br> If a method's + * Code attribute does not have a StackMapTable attribute, it has an implicit + * stack map attribute. This implicit stack map attribute is equivalent to a + * StackMapTable attribute with number_of_entries equal to zero. A method's Code + * attribute may have at most one StackMapTable attribute, otherwise a + * java.lang.ClassFormatError is thrown. <br> <br> The format of the stack map + * in the class file is given below. In the following, if the length of the + * method's byte code is 65535 or less, then uoffset represents the type u2; + * otherwise uoffset represents the type u4. If the maximum number of local + * variables for the method is 65535 or less, then <code>ulocalvar</code> + * represents the type u2; otherwise ulocalvar represents the type u4. If the + * maximum size of the operand stack is 65535 or less, then <code>ustack</code> + * represents the type u2; otherwise ustack represents the type u4. + * + * <pre> + * stack_map { // attribute StackMapTable + * u2 attribute_name_index; + * u4 attribute_length + * uoffset number_of_entries; + * stack_map_frame entries[number_of_entries]; + * } + * </pre> + * + * Each stack_map_frame structure specifies the type state at a particular byte + * code offset. Each frame type specifies (explicitly or implicitly) a value, + * offset_delta, that is used to calulate the actual byte code offset at which + * it applies. The byte code offset at which the frame applies is given by + * adding <code>1 + offset_delta</code> to the <code>offset</code> of the + * previous frame, unless the previous frame is the initial frame of the method, + * in which case the byte code offset is <code>offset_delta</code>. <br> <br> + * <i>Note that the length of the byte codes is not the same as the length of + * the Code attribute. The byte codes are embedded in the Code attribute, along + * with other information.</i> <br> <br> By using an offset delta rather than + * the actual byte code offset we ensure, by definition, that stack map frames + * are in the correctly sorted order. Furthermore, by consistently using the + * formula <code>offset_delta + 1</code> for all explicit frames, we guarantee + * the absence of duplicates. <br> <br> All frame types, even full_frame, rely + * on the previous frame for some of their semantics. This raises the question + * of what is the very first frame? The initial frame is implicit, and computed + * from the method descriptor. See the Prolog code for methodInitialStacFrame. + * <br> <br> The stack_map_frame structure consists of a one-byte tag followed + * by zero or more bytes, giving more information, depending upon the tag. <br> + * <br> A stack map frame may belong to one of several frame types + * + * <pre> + * union stack_map_frame { + * same_frame; + * same_locals_1_stack_item_frame; + * chop_frame; + * same_frame_extended; + * append_frame; + * full_frame; + * } + * </pre> + * + * The frame type same_frame is represented by tags in the range [0-63]. If the + * frame type is same_frame, it means the frame has exactly the same locals as + * the previous stack map frame and that the number of stack items is zero. The + * offset_delta value for the frame is the value of the tag field, frame_type. + * The form of such a frame is then: + * + * <pre> + * same_frame { + * u1 frame_type = SAME; // 0-63 + * } + * </pre> + * + * The frame type same_locals_1_stack_item_frame is represented by tags in the + * range [64, 127]. If the frame_type is same_locals_1_stack_item_frame, it + * means the frame has exactly the same locals as the previous stack map frame + * and that the number of stack items is 1. The offset_delta value for the frame + * is the value (frame_type - 64). There is a verification_type_info following + * the frame_type for the one stack item. The form of such a frame is then: + * + * <pre> + * same_locals_1_stack_item_frame { + * u1 frame_type = SAME_LOCALS_1_STACK_ITEM; // 64-127 + * verification_type_info stack[1]; + * } + * </pre> + * + * Tags in the range [128-247] are reserved for future use. <br> <br> The frame + * type chop_frame is represented by tags in the range [248-250]. If the + * frame_type is chop_frame, it means that the current locals are the same as + * the locals in the previous frame, except that the k last locals are absent. + * The value of k is given by the formula 251-frame_type. <br> <br> The form of + * such a frame is then: + * + * <pre> + * chop_frame { + * u1 frame_type=CHOP; // 248-250 + * uoffset offset_delta; + * } + * </pre> + * + * The frame type same_frame_extended is represented by the tag value 251. If + * the frame type is same_frame_extended, it means the frame has exactly the + * same locals as the previous stack map frame and that the number of stack + * items is zero. The form of such a frame is then: + * + * <pre> + * same_frame_extended { + * u1 frame_type = SAME_FRAME_EXTENDED; // 251 + * uoffset offset_delta; + * } + * </pre> + * + * The frame type append_frame is represented by tags in the range [252-254]. If + * the frame_type is append_frame, it means that the current locals are the same + * as the locals in the previous frame, except that k additional locals are + * defined. The value of k is given by the formula frame_type-251. <br> <br> The + * form of such a frame is then: + * + * <pre> + * append_frame { + * u1 frame_type =APPEND; // 252-254 + * uoffset offset_delta; + * verification_type_info locals[frame_type -251]; + * } + * </pre> + * + * The 0th entry in locals represents the type of the first additional local + * variable. If locals[M] represents local variable N, then locals[M+1] + * represents local variable N+1 if locals[M] is one of Top_variable_info, + * Integer_variable_info, Float_variable_info, Null_variable_info, + * UninitializedThis_variable_info, Object_variable_info, or + * Uninitialized_variable_info, otherwise locals[M+1] represents local variable + * N+2. It is an error if, for any index i, locals[i] represents a local + * variable whose index is greater than the maximum number of local variables + * for the method. <br> <br> The frame type full_frame is represented by the tag + * value 255. The form of such a frame is then: + * + * <pre> + * full_frame { + * u1 frame_type = FULL_FRAME; // 255 + * uoffset offset_delta; + * ulocalvar number_of_locals; + * verification_type_info locals[number_of_locals]; + * ustack number_of_stack_items; + * verification_type_info stack[number_of_stack_items]; + * } + * </pre> + * + * The 0th entry in locals represents the type of local variable 0. If locals[M] + * represents local variable N, then locals[M+1] represents local variable N+1 + * if locals[M] is one of Top_variable_info, Integer_variable_info, + * Float_variable_info, Null_variable_info, UninitializedThis_variable_info, + * Object_variable_info, or Uninitialized_variable_info, otherwise locals[M+1] + * represents local variable N+2. It is an error if, for any index i, locals[i] + * represents a local variable whose index is greater than the maximum number of + * local variables for the method. <br> <br> The 0th entry in stack represents + * the type of the bottom of the stack, and subsequent entries represent types + * of stack elements closer to the top of the operand stack. We shall refer to + * the bottom element of the stack as stack element 0, and to subsequent + * elements as stack element 1, 2 etc. If stack[M] represents stack element N, + * then stack[M+1] represents stack element N+1 if stack[M] is one of + * Top_variable_info, Integer_variable_info, Float_variable_info, + * Null_variable_info, UninitializedThis_variable_info, Object_variable_info, or + * Uninitialized_variable_info, otherwise stack[M+1] represents stack element + * N+2. It is an error if, for any index i, stack[i] represents a stack entry + * whose index is greater than the maximum operand stack size for the method. + * <br> <br> We say that an instruction in the byte code has a corresponding + * stack map frame if the offset in the offset field of the stack map frame is + * the same as the offset of the instruction in the byte codes. <br> <br> The + * verification_type_info structure consists of a one-byte tag followed by zero + * or more bytes, giving more information about the tag. Each + * verification_type_info structure specifies the verification type of one or + * two locations. + * + * <pre> + * union verification_type_info { + * Top_variable_info; + * Integer_variable_info; + * Float_variable_info; + * Long_variable_info; + * Double_variable_info; + * Null_variable_info; + * UninitializedThis_variable_info; + * Object_variable_info; + * Uninitialized_variable_info; + * } + * </pre> + * + * The Top_variable_info type indicates that the local variable has the + * verification type top (T.) + * + * <pre> + * Top_variable_info { + * u1 tag = ITEM_Top; // 0 + * } + * </pre> + * + * The Integer_variable_info type indicates that the location contains the + * verification type int. + * + * <pre> + * Integer_variable_info { + * u1 tag = ITEM_Integer; // 1 + * } + * </pre> + * + * The Float_variable_info type indicates that the location contains the + * verification type float. + * + * <pre> + * Float_variable_info { + * u1 tag = ITEM_Float; // 2 + * } + * </pre> + * + * The Long_variable_info type indicates that the location contains the + * verification type long. If the location is a local variable, then: + * + * <ul> <li>It must not be the local variable with the highest index.</li> + * <li>The next higher numbered local variable contains the verification type + * T.</li> </ul> + * + * If the location is an operand stack entry, then: + * + * <ul> <li>The current location must not be the topmost location of the + * operand stack.</li> <li>the next location closer to the top of the operand + * stack contains the verification type T.</li> </ul> + * + * This structure gives the contents of two locations in the operand stack or in + * the local variables. + * + * <pre> + * Long_variable_info { + * u1 tag = ITEM_Long; // 4 + * } + * </pre> + * + * The Double_variable_info type indicates that the location contains the + * verification type double. If the location is a local variable, then: + * + * <ul> <li>It must not be the local variable with the highest index.</li> + * <li>The next higher numbered local variable contains the verification type + * T. <li> </ul> + * + * If the location is an operand stack entry, then: + * + * <ul> <li>The current location must not be the topmost location of the + * operand stack.</li> <li>the next location closer to the top of the operand + * stack contains the verification type T.</li> </ul> + * + * This structure gives the contents of two locations in in the operand stack or + * in the local variables. + * + * <pre> + * Double_variable_info { + * u1 tag = ITEM_Double; // 3 + * } + * </pre> + * + * The Null_variable_info type indicates that location contains the verification + * type null. + * + * <pre> + * Null_variable_info { + * u1 tag = ITEM_Null; // 5 + * } + * </pre> + * + * The UninitializedThis_variable_info type indicates that the location contains + * the verification type uninitializedThis. + * + * <pre> + * UninitializedThis_variable_info { + * u1 tag = ITEM_UninitializedThis; // 6 + * } + * </pre> + * + * The Object_variable_info type indicates that the location contains an + * instance of the class referenced by the constant pool entry. + * + * <pre> + * Object_variable_info { + * u1 tag = ITEM_Object; // 7 + * u2 cpool_index; + * } + * </pre> + * + * The Uninitialized_variable_info indicates that the location contains the + * verification type uninitialized(offset). The offset item indicates the offset + * of the new instruction that created the object being stored in the location. + * + * <pre> + * Uninitialized_variable_info { + * u1 tag = ITEM_Uninitialized // 8 + * uoffset offset; + * } + * </pre> + * + * @see "ClassFileFormat-Java6.fm Page 138 Friday, April 15, 2005 3:22 PM" + * + * @author Eugene Kuleshov + */ +public class StackMapTableAttribute extends Attribute { + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. + */ + public static final int SAME_FRAME = 0; // to 63 (0-3f) + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1 + */ + public static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 + + // (40-7f) + + /** + * Reserved for future use + */ + public static final int RESERVED = 128; + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1. Offset is bigger then 63; + */ + public static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 + + /** + * Frame where current locals are the same as the locals in the previous + * frame, except that the k last locals are absent. The value of k is given + * by the formula 251-frame_type. + */ + public static final int CHOP_FRAME = 248; // to 250 (f8-fA) + + /** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. Offset is bigger then 63; + */ + public static final int SAME_FRAME_EXTENDED = 251; // fb + + /** + * Frame where current locals are the same as the locals in the previous + * frame, except that k additional locals are defined. The value of k is + * given by the formula frame_type-251. + */ + public static final int APPEND_FRAME = 252; // to 254 // fc-fe + + /** + * Full frame + */ + public static final int FULL_FRAME = 255; // ff + + private static final int MAX_SHORT = 65535; + + /** + * A <code>List</code> of <code>StackMapFrame</code> instances. + */ + private List frames; + + public StackMapTableAttribute() { + super("StackMapTable"); + } + + public StackMapTableAttribute(List frames) { + this(); + this.frames = frames; + } + + public List getFrames() { + return frames; + } + + public StackMapFrame getFrame(Label label) { + for (int i = 0; i < frames.size(); i++) { + StackMapFrame frame = (StackMapFrame) frames.get(i); + if (frame.label == label) { + return frame; + } + } + return null; + } + + public boolean isUnknown() { + return false; + } + + public boolean isCodeAttribute() { + return true; + } + + protected Attribute read( + ClassReader cr, + int off, + int len, + char[] buf, + int codeOff, + Label[] labels) + { + + ArrayList frames = new ArrayList(); + + // note that this is not the size of Code attribute + boolean isExtCodeSize = cr.readInt(codeOff + 4) > MAX_SHORT; + boolean isExtLocals = cr.readUnsignedShort(codeOff + 2) > MAX_SHORT; + boolean isExtStack = cr.readUnsignedShort(codeOff) > MAX_SHORT; + + int offset = 0; + + int methodOff = getMethodOff(cr, codeOff, buf); + StackMapFrame frame = new StackMapFrame(getLabel(offset, labels), + calculateLocals(cr.readClass(cr.header + 2, buf), // owner + cr.readUnsignedShort(methodOff), // method access + cr.readUTF8(methodOff + 2, buf), // method name + cr.readUTF8(methodOff + 4, buf)), // method desc + Collections.EMPTY_LIST); + frames.add(frame); + + // System.err.println( cr.readUTF8( methodOff + 2, buf)); + // System.err.println( offset +" delta:" + 0 +" : "+ frame); + + int size; + if (isExtCodeSize) { + size = cr.readInt(off); + off += 4; + } else { + size = cr.readUnsignedShort(off); + off += 2; + } + + for (; size > 0; size--) { + int tag = cr.readByte(off); // & 0xff; + off++; + + List stack; + List locals; + + int offsetDelta; + if (tag < SAME_LOCALS_1_STACK_ITEM_FRAME) { // SAME_FRAME + offsetDelta = tag; + + locals = new ArrayList(frame.locals); + stack = Collections.EMPTY_LIST; + + } else if (tag < RESERVED) { // SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta = tag - SAME_LOCALS_1_STACK_ITEM_FRAME; + + locals = new ArrayList(frame.locals); + stack = new ArrayList(); + // read verification_type_info stack[1]; + off = readType(stack, isExtCodeSize, cr, off, labels, buf); + + } else { + if (isExtCodeSize) { + offsetDelta = cr.readInt(off); + off += 4; + } else { + offsetDelta = cr.readUnsignedShort(off); + off += 2; + } + + if (tag == SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { // SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED + locals = new ArrayList(frame.locals); + stack = new ArrayList(); + // read verification_type_info stack[1]; + off = readType(stack, isExtCodeSize, cr, off, labels, buf); + + } else if (tag >= CHOP_FRAME && tag < SAME_FRAME_EXTENDED) { // CHOP_FRAME + stack = Collections.EMPTY_LIST; + + int k = SAME_FRAME_EXTENDED - tag; + // copy locals from prev frame and chop last k + locals = new ArrayList(frame.locals.subList(0, + frame.locals.size() - k)); + + } else if (tag == SAME_FRAME_EXTENDED) { // SAME_FRAME_EXTENDED + stack = Collections.EMPTY_LIST; + locals = new ArrayList(frame.locals); + + } else if ( /* tag>=APPEND && */tag < FULL_FRAME) { // APPEND_FRAME + stack = Collections.EMPTY_LIST; + + // copy locals from prev frame and append new k + locals = new ArrayList(frame.locals); + for (int k = tag - SAME_FRAME_EXTENDED; k > 0; k--) { + off = readType(locals, + isExtCodeSize, + cr, + off, + labels, + buf); + } + + } else if (tag == FULL_FRAME) { // FULL_FRAME + // read verification_type_info locals[number_of_locals]; + locals = new ArrayList(); + off = readTypes(locals, + isExtLocals, + isExtCodeSize, + cr, + off, + labels, + buf); + + // read verification_type_info stack[number_of_stack_items]; + stack = new ArrayList(); + off = readTypes(stack, + isExtStack, + isExtCodeSize, + cr, + off, + labels, + buf); + + } else { + throw new RuntimeException("Unknown frame type " + tag + + " after offset " + offset); + + } + } + + offset += offsetDelta; + + Label offsetLabel = getLabel(offset, labels); + + frame = new StackMapFrame(offsetLabel, locals, stack); + frames.add(frame); + // System.err.println( tag +" " + offset +" delta:" + offsetDelta + + // " frameType:"+ frameType+" : "+ frame); + + offset++; + } + + return new StackMapTableAttribute(frames); + } + + protected ByteVector write( + ClassWriter cw, + byte[] code, + int len, + int maxStack, + int maxLocals) + { + ByteVector bv = new ByteVector(); + // TODO verify this value (MAX_SHORT) + boolean isExtCodeSize = code != null && code.length > MAX_SHORT; + writeSize(frames.size() - 1, bv, isExtCodeSize); + + if (frames.size() < 2) { + return bv; + } + + boolean isExtLocals = maxLocals > MAX_SHORT; + boolean isExtStack = maxStack > MAX_SHORT; + + // skip the first frame + StackMapFrame frame = (StackMapFrame) frames.get(0); + List locals = frame.locals; + int offset = frame.label.getOffset(); + + for (int i = 1; i < frames.size(); i++) { + frame = (StackMapFrame) frames.get(i); + + List clocals = frame.locals; + List cstack = frame.stack; + int coffset = frame.label.getOffset(); + + int clocalsSize = clocals.size(); + int cstackSize = cstack.size(); + + int localsSize = locals.size(); + + int delta = coffset - offset; + + int type = FULL_FRAME; + int k = 0; + if (cstackSize == 0) { + k = clocalsSize - localsSize; + switch (k) { + case -3: + case -2: + case -1: + type = CHOP_FRAME; // CHOP or FULL + localsSize = clocalsSize; // for full_frame check + break; + + case 0: + // SAME, SAME_EXTENDED or FULL + type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; + break; + + case 1: + case 2: + case 3: + type = APPEND_FRAME; // APPEND or FULL + break; + } + } else if (localsSize == clocalsSize && cstackSize == 1) { + // SAME_LOCAL_1_STACK or FULL + type = delta < 63 + ? SAME_LOCALS_1_STACK_ITEM_FRAME + : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; + } + + if (type != FULL_FRAME) { + // verify if stack and locals are the same + for (int j = 0; j < localsSize && type != FULL_FRAME; j++) { + if (!locals.get(j).equals(clocals.get(j))) + type = FULL_FRAME; + } + } + + switch (type) { + case SAME_FRAME: + bv.putByte(delta); + break; + + case SAME_LOCALS_1_STACK_ITEM_FRAME: + bv.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); + writeTypeInfos(bv, cw, cstack, 0, 1); + break; + + case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: + bv.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED); + writeSize(delta, bv, isExtCodeSize); + writeTypeInfos(bv, cw, cstack, 0, 1); + break; + + case SAME_FRAME_EXTENDED: + bv.putByte(SAME_FRAME_EXTENDED); + writeSize(delta, bv, isExtCodeSize); + break; + + case CHOP_FRAME: + bv.putByte(SAME_FRAME_EXTENDED + k); // negative k + writeSize(delta, bv, isExtCodeSize); + break; + + case APPEND_FRAME: + bv.putByte(SAME_FRAME_EXTENDED + k); // positive k + writeSize(delta, bv, isExtCodeSize); + writeTypeInfos(bv, + cw, + clocals, + clocalsSize - 1, + clocalsSize); + break; + + case FULL_FRAME: + bv.putByte(FULL_FRAME); + writeSize(delta, bv, isExtCodeSize); + writeSize(clocalsSize, bv, isExtLocals); + writeTypeInfos(bv, cw, clocals, 0, clocalsSize); + writeSize(cstackSize, bv, isExtStack); + writeTypeInfos(bv, cw, cstack, 0, cstackSize); + break; + + default: + throw new RuntimeException(); + } + offset = coffset + 1; // compensating non first offset + locals = clocals; + } + return bv; + } + + private void writeSize(int delta, ByteVector bv, boolean isExt) { + if (isExt) { + bv.putInt(delta); + } else { + bv.putShort(delta); + } + } + + private void writeTypeInfos( + ByteVector bv, + ClassWriter cw, + List info, + int start, + int end) + { + for (int j = start; j < end; j++) { + StackMapType typeInfo = (StackMapType) info.get(j); + bv.putByte(typeInfo.getType()); + + switch (typeInfo.getType()) { + case StackMapType.ITEM_Object: // + bv.putShort(cw.newClass(typeInfo.getObject())); + break; + + case StackMapType.ITEM_Uninitialized: // + bv.putShort(typeInfo.getLabel().getOffset()); + break; + + } + } + } + + public static int getMethodOff(ClassReader cr, int codeOff, char[] buf) { + int off = cr.header + 6; + + int interfacesCount = cr.readUnsignedShort(off); + off += 2 + interfacesCount * 2; + + int fieldsCount = cr.readUnsignedShort(off); + off += 2; + for (; fieldsCount > 0; --fieldsCount) { + int attrCount = cr.readUnsignedShort(off + 6); // field attributes + off += 8; + for (; attrCount > 0; --attrCount) { + off += 6 + cr.readInt(off + 2); + } + } + + int methodsCount = cr.readUnsignedShort(off); + off += 2; + for (; methodsCount > 0; --methodsCount) { + int methodOff = off; + int attrCount = cr.readUnsignedShort(off + 6); // method attributes + off += 8; + for (; attrCount > 0; --attrCount) { + String attrName = cr.readUTF8(off, buf); + off += 6; + if (attrName.equals("Code")) { + if (codeOff == off) { + return methodOff; + } + } + off += cr.readInt(off - 4); + } + } + + return -1; + } + + /** + * Use method signature and access flags to resolve initial locals state. + * + * @param className name of the method's owner class. + * @param access access flags of the method. + * @param methodName name of the method. + * @param methodDesc descriptor of the method. + * @return list of <code>StackMapType</code> instances representing locals + * for an initial frame. + */ + public static List calculateLocals( + String className, + int access, + String methodName, + String methodDesc) + { + List locals = new ArrayList(); + + // TODO + if ("<init>".equals(methodName) + && !className.equals("java/lang/Object")) + { + StackMapType typeInfo = StackMapType.getTypeInfo(StackMapType.ITEM_UninitializedThis); + typeInfo.setObject(className); // this + locals.add(typeInfo); + } else if ((access & Opcodes.ACC_STATIC) == 0) { + StackMapType typeInfo = StackMapType.getTypeInfo(StackMapType.ITEM_Object); + typeInfo.setObject(className); // this + locals.add(typeInfo); + } + + Type[] types = Type.getArgumentTypes(methodDesc); + for (int i = 0; i < types.length; i++) { + Type t = types[i]; + StackMapType smt; + switch (t.getSort()) { + case Type.LONG: + smt = StackMapType.getTypeInfo(StackMapType.ITEM_Long); + break; + case Type.DOUBLE: + smt = StackMapType.getTypeInfo(StackMapType.ITEM_Double); + break; + + case Type.FLOAT: + smt = StackMapType.getTypeInfo(StackMapType.ITEM_Float); + break; + + case Type.ARRAY: + case Type.OBJECT: + smt = StackMapType.getTypeInfo(StackMapType.ITEM_Object); + smt.setObject(t.getDescriptor()); // TODO verify name + break; + + default: + smt = StackMapType.getTypeInfo(StackMapType.ITEM_Integer); + break; + } + } + + return locals; + } + + private int readTypes( + List info, + boolean isExt, + boolean isExtCodeSize, + ClassReader cr, + int off, + Label[] labels, + char[] buf) + { + int n = 0; + if (isExt) { + n = cr.readInt(off); + off += 4; + } else { + n = cr.readUnsignedShort(off); + off += 2; + } + + for (; n > 0; n--) { + off = readType(info, isExtCodeSize, cr, off, labels, buf); + } + return off; + } + + private int readType( + List info, + boolean isExtCodeSize, + ClassReader cr, + int off, + Label[] labels, + char[] buf) + { + int itemType = cr.readByte(off++); + StackMapType typeInfo = StackMapType.getTypeInfo(itemType); + info.add(typeInfo); + switch (itemType) { + // case StackMapType.ITEM_Long: // + // case StackMapType.ITEM_Double: // + // info.add(StackMapType.getTypeInfo(StackMapType.ITEM_Top)); + // break; + + case StackMapType.ITEM_Object: // + typeInfo.setObject(cr.readClass(off, buf)); + off += 2; + break; + + case StackMapType.ITEM_Uninitialized: // + int offset; + if (isExtCodeSize) { + offset = cr.readInt(off); + off += 4; + } else { + offset = cr.readUnsignedShort(off); + off += 2; + } + + typeInfo.setLabel(getLabel(offset, labels)); + break; + } + return off; + } + + private Label getLabel(int offset, Label[] labels) { + Label l = labels[offset]; + if (l != null) { + return l; + } + return labels[offset] = new Label(); + } + + public String toString() { + StringBuffer sb = new StringBuffer("StackMapTable["); + for (int i = 0; i < frames.size(); i++) { + sb.append('\n').append('[').append(frames.get(i)).append(']'); + } + sb.append("\n]"); + return sb.toString(); + } +}
diff --git a/asmx/src/org/objectweb/asm/attrs/StackMapType.java b/asmx/src/org/objectweb/asm/attrs/StackMapType.java new file mode 100644 index 0000000..ae3b8ce --- /dev/null +++ b/asmx/src/org/objectweb/asm/attrs/StackMapType.java
@@ -0,0 +1,114 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.objectweb.asm.attrs; + +import org.objectweb.asm.Label; + +/** + * Verification type info used by {@link StackMapAttribute}. + * + * @see <a href="http://www.jcp.org/en/jsr/detail?id=139">JSR 139 : Connected + * Limited Device Configuration 1.1</a> + * + * @see "ClassFileFormat-Java6.fm Page 138 Friday, April 15, 2005 3:22 PM" + * + * @author Eugene Kuleshov + */ + +public class StackMapType { + + public static final int ITEM_Top = 0; + public static final int ITEM_Integer = 1; + public static final int ITEM_Float = 2; + public static final int ITEM_Double = 3; + public static final int ITEM_Long = 4; + public static final int ITEM_Null = 5; + public static final int ITEM_UninitializedThis = 6; + public static final int ITEM_Object = 7; + public static final int ITEM_Uninitialized = 8; + + public static final String[] ITEM_NAMES = { + "Top", + "Integer", + "Float", + "Double", + "Long", + "Null", + "UninitializedThis", + "Object", + "Uninitialized" }; + + private int type; + private Label offset; + private String object; + + private StackMapType(int type) { + this.type = type; + } + + public int getType() { + return type; + } + + public static StackMapType getTypeInfo(int itemType) { + if (itemType < ITEM_Top || itemType > ITEM_Uninitialized) { + throw new IllegalArgumentException("" + itemType); + } + return new StackMapType(itemType); + } + + public void setLabel(Label offset) { + this.offset = offset; + } + + public void setObject(String object) { + this.object = object; + } + + public Label getLabel() { + return offset; + } + + public String getObject() { + return object; + } + + public String toString() { + StringBuffer sb = new StringBuffer(ITEM_NAMES[type]); + if (type == ITEM_Object) { + sb.append(":").append(object); + } + if (type == ITEM_Uninitialized) { + sb.append(":L").append(System.identityHashCode(offset)); + } + return sb.toString(); + } +}
diff --git a/asmx/src/org/objectweb/asm/commons/AdviceAdapter.java b/asmx/src/org/objectweb/asm/commons/AdviceAdapter.java new file mode 100644 index 0000000..141c8e2 --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/AdviceAdapter.java
@@ -0,0 +1,643 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A <code>MethodAdapter</code> to dispatch method body instruction + * <p> + * The behavior is like this: + * <ol> + * + * <li>as long as the INVOKESPECIAL for the object initialization has not been + * reached, every bytecode instruction is dispatched in the ctor code visitor</li> + * + * <li>when this one is reached, it is only added in the ctor code visitor and + * a JP invoke is added</li> + * <li>after that, only the other code visitor receives the instructions</li> + * + * </ol> + * + * @author Eugene Kuleshov + * @author Eric Bruneton + */ +public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes { + private static final Object THIS = new Object(); + private static final Object OTHER = new Object(); + + protected int methodAccess; + protected String methodDesc; + + private boolean constructor; + private boolean superInitialized; + private ArrayList stackFrame; + private HashMap branches; + + + /** + * Creates a new {@link AdviceAdapter}. + * + * @param mv the method visitor to which this adapter delegates calls. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + */ + public AdviceAdapter(MethodVisitor mv, int access, String name, String desc) { + super(mv, access, name, desc); + methodAccess = access; + methodDesc = desc; + + constructor = "<init>".equals(name); + if (!constructor) { + superInitialized = true; + onMethodEnter(); + } else { + stackFrame = new ArrayList(); + branches = new HashMap(); + } + } + + public void visitLabel(Label label) { + mv.visitLabel(label); + + if (constructor && branches != null) { + ArrayList frame = (ArrayList) branches.get(label); + if (frame != null) { + stackFrame = frame; + branches.remove(label); + } + } + } + + public void visitInsn(int opcode) { + if (constructor) { + switch (opcode) { + case RETURN: // empty stack + onMethodExit(opcode); + break; + + case IRETURN: // 1 before n/a after + case FRETURN: // 1 before n/a after + case ARETURN: // 1 before n/a after + case ATHROW: // 1 before n/a after + popValue(); + popValue(); + onMethodExit(opcode); + break; + + case LRETURN: // 2 before n/a after + case DRETURN: // 2 before n/a after + popValue(); + popValue(); + onMethodExit(opcode); + break; + + case NOP: + case LALOAD: // remove 2 add 2 + case DALOAD: // remove 2 add 2 + case LNEG: + case DNEG: + case FNEG: + case INEG: + case L2D: + case D2L: + case F2I: + case I2B: + case I2C: + case I2S: + case I2F: + case Opcodes.ARRAYLENGTH: + break; + + case ACONST_NULL: + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + case FCONST_0: + case FCONST_1: + case FCONST_2: + case F2L: // 1 before 2 after + case F2D: + case I2L: + case I2D: + pushValue(OTHER); + break; + + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + pushValue(OTHER); + pushValue(OTHER); + break; + + case IALOAD: // remove 2 add 1 + case FALOAD: // remove 2 add 1 + case AALOAD: // remove 2 add 1 + case BALOAD: // remove 2 add 1 + case CALOAD: // remove 2 add 1 + case SALOAD: // remove 2 add 1 + case POP: + case IADD: + case FADD: + case ISUB: + case LSHL: // 3 before 2 after + case LSHR: // 3 before 2 after + case LUSHR: // 3 before 2 after + case L2I: // 2 before 1 after + case L2F: // 2 before 1 after + case D2I: // 2 before 1 after + case D2F: // 2 before 1 after + case FSUB: + case FMUL: + case FDIV: + case FREM: + case FCMPL: // 2 before 1 after + case FCMPG: // 2 before 1 after + case IMUL: + case IDIV: + case IREM: + case ISHL: + case ISHR: + case IUSHR: + case IAND: + case IOR: + case IXOR: + case MONITORENTER: + case MONITOREXIT: + popValue(); + break; + + case POP2: + case LSUB: + case LMUL: + case LDIV: + case LREM: + case LADD: + case LAND: + case LOR: + case LXOR: + case DADD: + case DMUL: + case DSUB: + case DDIV: + case DREM: + popValue(); + popValue(); + break; + + case IASTORE: + case FASTORE: + case AASTORE: + case BASTORE: + case CASTORE: + case SASTORE: + case LCMP: // 4 before 1 after + case DCMPL: + case DCMPG: + popValue(); + popValue(); + popValue(); + break; + + case LASTORE: + case DASTORE: + popValue(); + popValue(); + popValue(); + popValue(); + break; + + case DUP: + pushValue(peekValue()); + break; + + case DUP_X1: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + pushValue(o1); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP_X2: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + Object o3 = popValue(); + pushValue(o1); + pushValue(o3); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP2: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + pushValue(o2); + pushValue(o1); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP2_X1: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + Object o3 = popValue(); + pushValue(o2); + pushValue(o1); + pushValue(o3); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP2_X2: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + Object o3 = popValue(); + Object o4 = popValue(); + pushValue(o2); + pushValue(o1); + pushValue(o4); + pushValue(o3); + pushValue(o2); + pushValue(o1); + } + break; + + case SWAP: { + Object o1 = popValue(); + Object o2 = popValue(); + pushValue(o1); + pushValue(o2); + } + break; + } + } else { + switch (opcode) { + case RETURN: + case IRETURN: + case FRETURN: + case ARETURN: + case LRETURN: + case DRETURN: + case ATHROW: + onMethodExit(opcode); + break; + } + } + mv.visitInsn(opcode); + } + + public void visitVarInsn(int opcode, int var) { + super.visitVarInsn(opcode, var); + + if (constructor) { + switch (opcode) { + case ILOAD: + case FLOAD: + pushValue(OTHER); + break; + case LLOAD: + case DLOAD: + pushValue(OTHER); + pushValue(OTHER); + break; + case ALOAD: + pushValue(var == 0 ? THIS : OTHER); + break; + case ASTORE: + case ISTORE: + case FSTORE: + popValue(); + break; + case LSTORE: + case DSTORE: + popValue(); + popValue(); + break; + } + } + } + + public void visitFieldInsn( + int opcode, + String owner, + String name, + String desc) + { + mv.visitFieldInsn(opcode, owner, name, desc); + + if (constructor) { + char c = desc.charAt(0); + boolean longOrDouble = c == 'J' || c == 'D'; + switch (opcode) { + case GETSTATIC: + pushValue(OTHER); + if (longOrDouble) { + pushValue(OTHER); + } + break; + case PUTSTATIC: + popValue(); + if(longOrDouble) { + popValue(); + } + break; + case PUTFIELD: + popValue(); + if(longOrDouble) { + popValue(); + popValue(); + } + break; + // case GETFIELD: + default: + if (longOrDouble) { + pushValue(OTHER); + } + } + } + } + + public void visitIntInsn(int opcode, int operand) { + mv.visitIntInsn(opcode, operand); + + if (constructor) { + switch (opcode) { + case BIPUSH: + case SIPUSH: + pushValue(OTHER); + } + } + } + + public void visitLdcInsn(Object cst) { + mv.visitLdcInsn(cst); + + if (constructor) { + pushValue(OTHER); + if (cst instanceof Double || cst instanceof Long) { + pushValue(OTHER); + } + } + } + + public void visitMultiANewArrayInsn(String desc, int dims) { + mv.visitMultiANewArrayInsn(desc, dims); + + if (constructor) { + for (int i = 0; i < dims; i++) { + popValue(); + } + pushValue(OTHER); + } + } + + public void visitTypeInsn(int opcode, String name) { + mv.visitTypeInsn(opcode, name); + + // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack + if (constructor && opcode == NEW) { + pushValue(OTHER); + } + } + + public void visitMethodInsn( + int opcode, + String owner, + String name, + String desc) + { + mv.visitMethodInsn(opcode, owner, name, desc); + + if (constructor) { + Type[] types = Type.getArgumentTypes(desc); + for (int i = 0; i < types.length; i++) { + popValue(); + if (types[i].getSize() == 2) { + popValue(); + } + } + switch (opcode) { + // case INVOKESTATIC: + // break; + + case INVOKEINTERFACE: + case INVOKEVIRTUAL: + popValue(); // objectref + break; + + case INVOKESPECIAL: + Object type = popValue(); // objectref + if (type == THIS && !superInitialized) { + onMethodEnter(); + superInitialized = true; + // once super has been initialized it is no longer + // necessary to keep track of stack state + constructor = false; + } + break; + } + + Type returnType = Type.getReturnType(desc); + if (returnType != Type.VOID_TYPE) { + pushValue(OTHER); + if (returnType.getSize() == 2) { + pushValue(OTHER); + } + } + } + } + + public void visitJumpInsn(int opcode, Label label) { + mv.visitJumpInsn(opcode, label); + + if (constructor) { + switch (opcode) { + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + case IFNULL: + case IFNONNULL: + popValue(); + break; + + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ACMPEQ: + case IF_ACMPNE: + popValue(); + popValue(); + break; + + case JSR: + pushValue(OTHER); + break; + } + addBranch(label); + } + } + + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + mv.visitLookupSwitchInsn(dflt, keys, labels); + + if (constructor) { + popValue(); + addBranches(dflt, labels); + } + } + + public void visitTableSwitchInsn( + int min, + int max, + Label dflt, + Label[] labels) + { + mv.visitTableSwitchInsn(min, max, dflt, labels); + + if (constructor) { + popValue(); + addBranches(dflt, labels); + } + } + + private void addBranches(Label dflt, Label[] labels) { + addBranch(dflt); + for (int i = 0; i < labels.length; i++) { + addBranch(labels[i]); + } + } + + private void addBranch(Label label) { + if (branches.containsKey(label)) { + return; + } + ArrayList frame = new ArrayList(); + frame.addAll(stackFrame); + branches.put(label, frame); + } + + private Object popValue() { + return stackFrame.remove(stackFrame.size()-1); + } + + private Object peekValue() { + return stackFrame.get(stackFrame.size()-1); + } + + private void pushValue(Object o) { + stackFrame.add(o); + } + + /** + * Called at the beginning of the method or after super + * class class call in the constructor. + * <br><br> + * + * <i>Custom code can use or change all the local variables, + * but should not change state of the stack.</i> + */ + protected abstract void onMethodEnter(); + + /** + * Called before explicit exit from the method using either + * return or throw. Top element on the stack contains the + * return value or exception instance. For example: + * + * <pre> + * public void onMethodExit(int opcode) { + * if(opcode==RETURN) { + * visitInsn(ACONST_NULL); + * } else if(opcode==ARETURN || opcode==ATHROW) { + * dup(); + * } else { + * if(opcode==LRETURN || opcode==DRETURN) { + * dup2(); + * } else { + * dup(); + * } + * box(Type.getReturnType(this.methodDesc)); + * } + * visitIntInsn(SIPUSH, opcode); + * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V"); + * } + * + * // an actual call back method + * public static void onExit(int opcode, Object param) { + * ... + * </pre> + * + * <br><br> + * + * <i>Custom code can use or change all the local variables, + * but should not change state of the stack.</i> + * + * @param opcode one of the RETURN, IRETURN, FRETURN, + * ARETURN, LRETURN, DRETURN or ATHROW + * + */ + protected abstract void onMethodExit(int opcode); + + // TODO onException, onMethodCall + +} +
diff --git a/asmx/src/org/objectweb/asm/commons/EmptyVisitor.java b/asmx/src/org/objectweb/asm/commons/EmptyVisitor.java new file mode 100644 index 0000000..33523e7 --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/EmptyVisitor.java
@@ -0,0 +1,277 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.PrecompiledMethodVisitor; +import org.objectweb.asm.TypePath; + +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +/** + * An empty implementation of the ASM visitor interfaces. + * + * @author Eric Bruneton + */ +public class EmptyVisitor implements + ClassVisitor, + FieldVisitor, + PrecompiledMethodVisitor, // changed from MethodVisitor + AnnotationVisitor, + TypeAnnotationVisitor // jaime +{ + + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) + { + } + + public void visitSource(String source, String debug) { + } + + public void visitOuterClass(String owner, String name, String desc) { + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return this; + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + return this; + } + //end jaime + + public void visitAttribute(Attribute attr) { + } + + public void visitInnerClass( + String name, + String outerName, + String innerName, + int access) + { + } + + public FieldVisitor visitField( + int access, + String name, + String desc, + String signature, + Object value) + { + return this; + } + + public MethodVisitor visitMethod( + int access, + String name, + String desc, + String signature, + String[] exceptions) + { + return this; + } + + public void visitEnd() { + } + + public AnnotationVisitor visitAnnotationDefault() { + return this; + } + + public AnnotationVisitor visitParameterAnnotation( + int parameter, + String desc, + boolean visible) + { + return this; + } + + public void visitCode() { + } + + public void visitCurrentPosition(int position) { + } + + public void visitInsn(int opcode) { + } + + public void visitIntInsn(int opcode, int operand) { + } + + public void visitVarInsn(int opcode, int var) { + } + + public void visitTypeInsn(int opcode, String desc) { + } + + public void visitFieldInsn( + int opcode, + String owner, + String name, + String desc) + { + } + + public void visitMethodInsn( + int opcode, + String owner, + String name, + String desc) + { + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + } + + public void visitJumpInsn(int opcode, Label label) { + } + + public void visitLabel(Label label) { + } + + public void visitLdcInsn(Object cst) { + } + + public void visitIincInsn(int var, int increment) { + } + + public void visitTableSwitchInsn( + int min, + int max, + Label dflt, + Label labels[]) + { + } + + public void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]) { + } + + public void visitMultiANewArrayInsn(String desc, int dims) { + } + + public void visitInvokeDynamicInsn(int a, int b) { + } + + @Override + public AnnotationVisitor visitInsnAnnotation( + int typeRef, + TypePath typePath, + String desc, + boolean visible) + { + return this; + } + + public void visitTryCatchBlock( + Label start, + Label end, + Label handler, + String type) + { + } + + public void visitLocalVariable( + String name, + String desc, + String signature, + Label start, + Label end, + int index) + { + } + + public void visitLineNumber(int line, Label start) { + } + + public void visitMaxs(int maxStack, int maxLocals) { + } + + public void visit(String name, Object value) { + } + + public void visitEnum(String name, String desc, String value) { + } + + public AnnotationVisitor visitAnnotation(String name, String desc) { + return this; + } + + public AnnotationVisitor visitArray(String name) { + return this; + } + + //jaime + public void visitXTargetType(int target_type) { + } + public void visitXOffset(int offset) { + } + public void visitXLocationLength(int location_length) { + } + public void visitXLocation(TypePathEntry location) { + } + public void visitXNumEntries(int num_entries) { + } + public void visitXStartPc(int start_pc) { + } + public void visitXLength(int length) { + } + public void visitXIndex(int index) { + } + public void visitXParamIndex(int param_index) { + } + public void visitXBoundIndex(int bound_index) { + } + public void visitXTypeIndex(int type_index) { + } + //end jaime + public void visitXExceptionIndex(int exception_index) { + } + public void visitXNameAndArgsSize() { + } +}
diff --git a/asmx/src/org/objectweb/asm/commons/GeneratorAdapter.java b/asmx/src/org/objectweb/asm/commons/GeneratorAdapter.java new file mode 100644 index 0000000..40b4db5 --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/GeneratorAdapter.java
@@ -0,0 +1,1454 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A {@link org.objectweb.asm.MethodAdapter} with convenient methods to generate + * code. For example, using this adapter, the class below + * + * <pre> + * public class Example { + * public static void main(String[] args) { + * System.out.println("Hello world!"); + * } + * } + * </pre> + * + * can be generated as follows: + * + * <pre> + * ClassWriter cw = new ClassWriter(true); + * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null); + * + * Method m = Method.getMethod("void <init> ()"); + * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); + * mg.loadThis(); + * mg.invokeConstructor(Type.getType(Object.class), m); + * mg.returnValue(); + * mg.endMethod(); + * + * m = Method.getMethod("void main (String[])"); + * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); + * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); + * mg.push("Hello world!"); + * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)")); + * mg.returnValue(); + * mg.endMethod(); + * + * cw.visitEnd(); + * </pre> + * + * @author Juozas Baliuka + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class GeneratorAdapter extends LocalVariablesSorter { + + private final static Type BYTE_TYPE = Type.getType("Ljava/lang/Byte;"); + + private final static Type BOOLEAN_TYPE = Type.getType("Ljava/lang/Boolean;"); + + private final static Type SHORT_TYPE = Type.getType("Ljava/lang/Short;"); + + private final static Type CHARACTER_TYPE = Type.getType("Ljava/lang/Character;"); + + private final static Type INTEGER_TYPE = Type.getType("Ljava/lang/Integer;"); + + private final static Type FLOAT_TYPE = Type.getType("Ljava/lang/Float;"); + + private final static Type LONG_TYPE = Type.getType("Ljava/lang/Long;"); + + private final static Type DOUBLE_TYPE = Type.getType("Ljava/lang/Double;"); + + private final static Type NUMBER_TYPE = Type.getType("Ljava/lang/Number;"); + + private final static Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); + + private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()"); + + private final static Method CHAR_VALUE = Method.getMethod("char charValue()"); + + private final static Method INT_VALUE = Method.getMethod("int intValue()"); + + private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()"); + + private final static Method LONG_VALUE = Method.getMethod("long longValue()"); + + private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()"); + + /** + * Constant for the {@link #math math} method. + */ + public final static int ADD = Opcodes.IADD; + + /** + * Constant for the {@link #math math} method. + */ + public final static int SUB = Opcodes.ISUB; + + /** + * Constant for the {@link #math math} method. + */ + public final static int MUL = Opcodes.IMUL; + + /** + * Constant for the {@link #math math} method. + */ + public final static int DIV = Opcodes.IDIV; + + /** + * Constant for the {@link #math math} method. + */ + public final static int REM = Opcodes.IREM; + + /** + * Constant for the {@link #math math} method. + */ + public final static int NEG = Opcodes.INEG; + + /** + * Constant for the {@link #math math} method. + */ + public final static int SHL = Opcodes.ISHL; + + /** + * Constant for the {@link #math math} method. + */ + public final static int SHR = Opcodes.ISHR; + + /** + * Constant for the {@link #math math} method. + */ + public final static int USHR = Opcodes.IUSHR; + + /** + * Constant for the {@link #math math} method. + */ + public final static int AND = Opcodes.IAND; + + /** + * Constant for the {@link #math math} method. + */ + public final static int OR = Opcodes.IOR; + + /** + * Constant for the {@link #math math} method. + */ + public final static int XOR = Opcodes.IXOR; + + /** + * Constant for the {@link #ifCmp ifCmp} method. + */ + public final static int EQ = Opcodes.IFEQ; + + /** + * Constant for the {@link #ifCmp ifCmp} method. + */ + public final static int NE = Opcodes.IFNE; + + /** + * Constant for the {@link #ifCmp ifCmp} method. + */ + public final static int LT = Opcodes.IFLT; + + /** + * Constant for the {@link #ifCmp ifCmp} method. + */ + public final static int GE = Opcodes.IFGE; + + /** + * Constant for the {@link #ifCmp ifCmp} method. + */ + public final static int GT = Opcodes.IFGT; + + /** + * Constant for the {@link #ifCmp ifCmp} method. + */ + public final static int LE = Opcodes.IFLE; + + /** + * Access flags of the method visited by this adapter. + */ + private final int access; + + /** + * Return type of the method visited by this adapter. + */ + private final Type returnType; + + /** + * Argument types of the method visited by this adapter. + */ + private final Type[] argumentTypes; + + /** + * Types of the local variables of the method visited by this adapter. + */ + private final List localTypes; + + /** + * Creates a new {@link GeneratorAdapter}. + * + * @param mv the method visitor to which this adapter delegates calls. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + */ + public GeneratorAdapter( + MethodVisitor mv, + int access, + String name, + String desc) + { + super(access, desc, mv); + this.access = access; + this.returnType = Type.getReturnType(desc); + this.argumentTypes = Type.getArgumentTypes(desc); + this.localTypes = new ArrayList(); + } + + /** + * Creates a new {@link GeneratorAdapter}. + * + * @param access access flags of the adapted method. + * @param method the adapted method. + * @param mv the method visitor to which this adapter delegates calls. + */ + public GeneratorAdapter( + final int access, + final Method method, + final MethodVisitor mv) + { + super(access, method.getDescriptor(), mv); + this.access = access; + this.returnType = method.getReturnType(); + this.argumentTypes = method.getArgumentTypes(); + this.localTypes = new ArrayList(); + } + + /** + * Creates a new {@link GeneratorAdapter}. + * + * @param access access flags of the adapted method. + * @param method the adapted method. + * @param signature the signature of the adapted method (may be + * <tt>null</tt>). + * @param exceptions the exceptions thrown by the adapted method (may be + * <tt>null</tt>). + * @param cv the class visitor to which this adapter delegates calls. + */ + public GeneratorAdapter( + final int access, + final Method method, + final String signature, + final Type[] exceptions, + final ClassVisitor cv) + { + this(access, method, cv.visitMethod(access, + method.getName(), + method.getDescriptor(), + signature, + getInternalNames(exceptions))); + } + + /** + * Returns the internal names of the given types. + * + * @param types a set of types. + * @return the internal names of the given types. + */ + private static String[] getInternalNames(final Type[] types) { + if (types == null) { + return null; + } + String[] names = new String[types.length]; + for (int i = 0; i < names.length; ++i) { + names[i] = types[i].getInternalName(); + } + return names; + } + + // ------------------------------------------------------------------------ + // Instructions to push constants on the stack + // ------------------------------------------------------------------------ + + /** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ + public void push(final boolean value) { + push(value ? 1 : 0); + } + + /** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ + public void push(final int value) { + if (value >= -1 && value <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + value); + } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { + mv.visitIntInsn(Opcodes.BIPUSH, value); + } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { + mv.visitIntInsn(Opcodes.SIPUSH, value); + } else { + mv.visitLdcInsn(new Integer(value)); + } + } + + /** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ + public void push(final long value) { + if (value == 0L || value == 1L) { + mv.visitInsn(Opcodes.LCONST_0 + (int) value); + } else { + mv.visitLdcInsn(new Long(value)); + } + } + + /** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ + public void push(final float value) { + int bits = Float.floatToIntBits(value); + if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2 + mv.visitInsn(Opcodes.FCONST_0 + (int) value); + } else { + mv.visitLdcInsn(new Float(value)); + } + } + + /** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ + public void push(final double value) { + long bits = Double.doubleToLongBits(value); + if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d + mv.visitInsn(Opcodes.DCONST_0 + (int) value); + } else { + mv.visitLdcInsn(new Double(value)); + } + } + + /** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. May be <tt>null</tt>. + */ + public void push(final String value) { + if (value == null) { + mv.visitInsn(Opcodes.ACONST_NULL); + } else { + mv.visitLdcInsn(value); + } + } + + /** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ + public void push(final Type value) { + if (value == null) { + mv.visitInsn(Opcodes.ACONST_NULL); + } else { + mv.visitLdcInsn(value); + } + } + + // ------------------------------------------------------------------------ + // Instructions to load and store method arguments + // ------------------------------------------------------------------------ + + /** + * Returns the index of the given method argument in the frame's local + * variables array. + * + * @param arg the index of a method argument. + * @return the index of the given method argument in the frame's local + * variables array. + */ + private int getArgIndex(final int arg) { + int index = ((access & Opcodes.ACC_STATIC) == 0 ? 1 : 0); + for (int i = 0; i < arg; i++) { + index += argumentTypes[i].getSize(); + } + return index; + } + + /** + * Generates the instruction to push a local variable on the stack. + * + * @param type the type of the local variable to be loaded. + * @param index an index in the frame's local variables array. + */ + private void loadInsn(final Type type, final int index) { + mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); + } + + /** + * Generates the instruction to store the top stack value in a local + * variable. + * + * @param type the type of the local variable to be stored. + * @param index an index in the frame's local variables array. + */ + private void storeInsn(final Type type, final int index) { + mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); + } + + /** + * Generates the instruction to load 'this' on the stack. + */ + public void loadThis() { + if ((access & Opcodes.ACC_STATIC) != 0) { + throw new IllegalStateException("no 'this' pointer within static method"); + } + mv.visitVarInsn(Opcodes.ALOAD, 0); + } + + /** + * Generates the instruction to load the given method argument on the stack. + * + * @param arg the index of a method argument. + */ + public void loadArg(final int arg) { + loadInsn(argumentTypes[arg], getArgIndex(arg)); + } + + /** + * Generates the instructions to load the given method arguments on the + * stack. + * + * @param arg the index of the first method argument to be loaded. + * @param count the number of method arguments to be loaded. + */ + public void loadArgs(final int arg, final int count) { + int index = getArgIndex(arg); + for (int i = 0; i < count; ++i) { + Type t = argumentTypes[arg + i]; + loadInsn(t, index); + index += t.getSize(); + } + } + + /** + * Generates the instructions to load all the method arguments on the stack. + */ + public void loadArgs() { + loadArgs(0, argumentTypes.length); + } + + /** + * Generates the instructions to load all the method arguments on the stack, + * as a single object array. + */ + public void loadArgArray() { + push(argumentTypes.length); + newArray(OBJECT_TYPE); + for (int i = 0; i < argumentTypes.length; i++) { + dup(); + push(i); + loadArg(i); + box(argumentTypes[i]); + arrayStore(OBJECT_TYPE); + } + } + + /** + * Generates the instruction to store the top stack value in the given + * method argument. + * + * @param arg the index of a method argument. + */ + public void storeArg(final int arg) { + storeInsn(argumentTypes[arg], getArgIndex(arg)); + } + + // ------------------------------------------------------------------------ + // Instructions to load and store local variables + // ------------------------------------------------------------------------ + + /** + * Creates a new local variable of the given type. + * + * @param type the type of the local variable to be created. + * @return the identifier of the newly created local variable. + */ + public int newLocal(final Type type) { + int local = super.newLocal(type.getSize()); + setLocalType(local, type); + return local; + } + + /** + * Returns the type of the given local variable. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal}. + * @return the type of the given local variable. + */ + public Type getLocalType(final int local) { + return (Type) localTypes.get(local - firstLocal); + } + + /** + * Sets the current type of the given local variable. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal}. + * @param type the type of the value being stored in the local variable + */ + private void setLocalType(final int local, final Type type) { + int index = local - firstLocal; + while (localTypes.size() < index + 1) + localTypes.add(null); + localTypes.set(index, type); + } + + /** + * Generates the instruction to load the given local variable on the stack. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal}. + */ + public void loadLocal(final int local) { + loadInsn(getLocalType(local), local); + } + + /** + * Generates the instruction to load the given local variable on the stack. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal}. + * @param type the type of this local variable. + */ + public void loadLocal(final int local, final Type type) { + setLocalType(local, type); + loadInsn(type, local); + } + + /** + * Generates the instruction to store the top stack value in the given local + * variable. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal}. + */ + public void storeLocal(final int local) { + storeInsn(getLocalType(local), local); + } + + /** + * Generates the instruction to store the top stack value in the given local + * variable. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal}. + * @param type the type of this local variable. + */ + public void storeLocal(final int local, final Type type) { + setLocalType(local, type); + storeInsn(type, local); + } + + /** + * Generates the instruction to load an element from an array. + * + * @param type the type of the array element to be loaded. + */ + public void arrayLoad(final Type type) { + mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); + } + + /** + * Generates the instruction to store an element in an array. + * + * @param type the type of the array element to be stored. + */ + public void arrayStore(final Type type) { + mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); + } + + // ------------------------------------------------------------------------ + // Instructions to manage the stack + // ------------------------------------------------------------------------ + + /** + * Generates a POP instruction. + */ + public void pop() { + mv.visitInsn(Opcodes.POP); + } + + /** + * Generates a POP2 instruction. + */ + public void pop2() { + mv.visitInsn(Opcodes.POP2); + } + + /** + * Generates a DUP instruction. + */ + public void dup() { + mv.visitInsn(Opcodes.DUP); + } + + /** + * Generates a DUP2 instruction. + */ + public void dup2() { + mv.visitInsn(Opcodes.DUP2); + } + + /** + * Generates a DUP_X1 instruction. + */ + public void dupX1() { + mv.visitInsn(Opcodes.DUP_X1); + } + + /** + * Generates a DUP_X2 instruction. + */ + public void dupX2() { + mv.visitInsn(Opcodes.DUP_X2); + } + + /** + * Generates a DUP2_X1 instruction. + */ + public void dup2X1() { + mv.visitInsn(Opcodes.DUP2_X1); + } + + /** + * Generates a DUP2_X2 instruction. + */ + public void dup2X2() { + mv.visitInsn(Opcodes.DUP2_X2); + } + + /** + * Generates a SWAP instruction. + */ + public void swap() { + mv.visitInsn(Opcodes.SWAP); + } + + /** + * Generates the instructions to swap the top two stack values. + * + * @param prev type of the top - 1 stack value. + * @param type type of the top stack value. + */ + public void swap(final Type prev, final Type type) { + if (type.getSize() == 1) { + if (prev.getSize() == 1) { + swap(); // same as dupX1(), pop(); + } else { + dupX2(); + pop(); + } + } else { + if (prev.getSize() == 1) { + dup2X1(); + pop2(); + } else { + dup2X2(); + pop2(); + } + } + } + + // ------------------------------------------------------------------------ + // Instructions to do mathematical and logical operations + // ------------------------------------------------------------------------ + + /** + * Generates the instruction to do the specified mathematical or logical + * operation. + * + * @param op a mathematical or logical operation. Must be one of ADD, SUB, + * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR. + * @param type the type of the operand(s) for this operation. + */ + public void math(final int op, final Type type) { + mv.visitInsn(type.getOpcode(op)); + } + + /** + * Generates the instructions to compute the bitwise negation of the top + * stack value. + */ + public void not() { + mv.visitInsn(Opcodes.ICONST_1); + mv.visitInsn(Opcodes.IXOR); + } + + /** + * Generates the instruction to increment the given local variable. + * + * @param local the local variable to be incremented. + * @param amount the amount by which the local variable must be incremented. + */ + public void iinc(final int local, final int amount) { + mv.visitIincInsn(local, amount); + } + + /** + * Generates the instructions to cast a numerical value from one type to + * another. + * + * @param from the type of the top stack value + * @param to the type into which this value must be cast. + */ + public void cast(final Type from, final Type to) { + if (from != to) { + if (from == Type.DOUBLE_TYPE) { + if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Opcodes.D2F); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Opcodes.D2L); + } else { + mv.visitInsn(Opcodes.D2I); + cast(Type.INT_TYPE, to); + } + } else if (from == Type.FLOAT_TYPE) { + if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Opcodes.F2D); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Opcodes.F2L); + } else { + mv.visitInsn(Opcodes.F2I); + cast(Type.INT_TYPE, to); + } + } else if (from == Type.LONG_TYPE) { + if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Opcodes.L2D); + } else if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Opcodes.L2F); + } else { + mv.visitInsn(Opcodes.L2I); + cast(Type.INT_TYPE, to); + } + } else { + if (to == Type.BYTE_TYPE) { + mv.visitInsn(Opcodes.I2B); + } else if (to == Type.CHAR_TYPE) { + mv.visitInsn(Opcodes.I2C); + } else if (to == Type.DOUBLE_TYPE) { + mv.visitInsn(Opcodes.I2D); + } else if (to == Type.FLOAT_TYPE) { + mv.visitInsn(Opcodes.I2F); + } else if (to == Type.LONG_TYPE) { + mv.visitInsn(Opcodes.I2L); + } else if (to == Type.SHORT_TYPE) { + mv.visitInsn(Opcodes.I2S); + } + } + } + } + + // ------------------------------------------------------------------------ + // Instructions to do boxing and unboxing operations + // ------------------------------------------------------------------------ + + /** + * Generates the instructions to box the top stack value. This value is + * replaced by its boxed equivalent on top of the stack. + * + * @param type the type of the top stack value. + */ + public void box(final Type type) { + if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { + return; + } + if (type == Type.VOID_TYPE) { + push((String) null); + } else { + Type boxed = type; + switch (type.getSort()) { + case Type.BYTE: + boxed = BYTE_TYPE; + break; + case Type.BOOLEAN: + boxed = BOOLEAN_TYPE; + break; + case Type.SHORT: + boxed = SHORT_TYPE; + break; + case Type.CHAR: + boxed = CHARACTER_TYPE; + break; + case Type.INT: + boxed = INTEGER_TYPE; + break; + case Type.FLOAT: + boxed = FLOAT_TYPE; + break; + case Type.LONG: + boxed = LONG_TYPE; + break; + case Type.DOUBLE: + boxed = DOUBLE_TYPE; + break; + } + newInstance(boxed); + if (type.getSize() == 2) { + // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o + dupX2(); + dupX2(); + pop(); + } else { + // p -> po -> opo -> oop -> o + dupX1(); + swap(); + } + invokeConstructor(boxed, new Method("<init>", + Type.VOID_TYPE, + new Type[] { type })); + } + } + + /** + * Generates the instructions to unbox the top stack value. This value is + * replaced by its unboxed equivalent on top of the stack. + * + * @param type the type of the top stack value. + */ + public void unbox(final Type type) { + Type t = NUMBER_TYPE; + Method sig = null; + switch (type.getSort()) { + case Type.VOID: + return; + case Type.CHAR: + t = CHARACTER_TYPE; + sig = CHAR_VALUE; + break; + case Type.BOOLEAN: + t = BOOLEAN_TYPE; + sig = BOOLEAN_VALUE; + break; + case Type.DOUBLE: + sig = DOUBLE_VALUE; + break; + case Type.FLOAT: + sig = FLOAT_VALUE; + break; + case Type.LONG: + sig = LONG_VALUE; + break; + case Type.INT: + case Type.SHORT: + case Type.BYTE: + sig = INT_VALUE; + } + if (sig == null) { + checkCast(type); + } else { + checkCast(t); + invokeVirtual(t, sig); + } + } + + // ------------------------------------------------------------------------ + // Instructions to jump to other instructions + // ------------------------------------------------------------------------ + + /** + * Creates a new {@link Label}. + * + * @return a new {@link Label}. + */ + public Label newLabel() { + return new Label(); + } + + /** + * Marks the current code position with the given label. + * + * @param label a label. + */ + public void mark(final Label label) { + mv.visitLabel(label); + } + + /** + * Marks the current code position with a new label. + * + * @return the label that was created to mark the current code position. + */ + public Label mark() { + Label label = new Label(); + mv.visitLabel(label); + return label; + } + + /** + * Generates the instructions to jump to a label based on the comparison of + * the top two stack values. + * + * @param type the type of the top two stack values. + * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, + * LE. + * @param label where to jump if the comparison result is <tt>true</tt>. + */ + public void ifCmp(final Type type, final int mode, final Label label) { + int intOp = -1; + int jumpMode = mode; + switch (mode) { + case GE: + jumpMode = LT; + break; + case LE: + jumpMode = GT; + break; + } + switch (type.getSort()) { + case Type.LONG: + mv.visitInsn(Opcodes.LCMP); + break; + case Type.DOUBLE: + mv.visitInsn(Opcodes.DCMPG); + break; + case Type.FLOAT: + mv.visitInsn(Opcodes.FCMPG); + break; + case Type.ARRAY: + case Type.OBJECT: + switch (mode) { + case EQ: + mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); + return; + case NE: + mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); + return; + } + throw new IllegalArgumentException("Bad comparison for type " + + type); + default: + switch (mode) { + case EQ: + intOp = Opcodes.IF_ICMPEQ; + break; + case NE: + intOp = Opcodes.IF_ICMPNE; + break; + case GE: + intOp = Opcodes.IF_ICMPGE; + break; + case LT: + intOp = Opcodes.IF_ICMPLT; + break; + case LE: + intOp = Opcodes.IF_ICMPLE; + break; + case GT: + intOp = Opcodes.IF_ICMPGT; + break; + } + mv.visitJumpInsn(intOp, label); + return; + } + mv.visitJumpInsn(jumpMode, label); + } + + /** + * Generates the instructions to jump to a label based on the comparison of + * the top two integer stack values. + * + * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, + * LE. + * @param label where to jump if the comparison result is <tt>true</tt>. + */ + public void ifICmp(final int mode, final Label label) { + ifCmp(Type.INT_TYPE, mode, label); + } + + /** + * Generates the instructions to jump to a label based on the comparison of + * the top integer stack value with zero. + * + * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, + * LE. + * @param label where to jump if the comparison result is <tt>true</tt>. + */ + public void ifZCmp(final int mode, final Label label) { + mv.visitJumpInsn(mode, label); + } + + /** + * Generates the instruction to jump to the given label if the top stack + * value is null. + * + * @param label where to jump if the condition is <tt>true</tt>. + */ + public void ifNull(final Label label) { + mv.visitJumpInsn(Opcodes.IFNULL, label); + } + + /** + * Generates the instruction to jump to the given label if the top stack + * value is not null. + * + * @param label where to jump if the condition is <tt>true</tt>. + */ + public void ifNonNull(final Label label) { + mv.visitJumpInsn(Opcodes.IFNONNULL, label); + } + + /** + * Generates the instruction to jump to the given label. + * + * @param label where to jump if the condition is <tt>true</tt>. + */ + public void goTo(final Label label) { + mv.visitJumpInsn(Opcodes.GOTO, label); + } + + /** + * Generates a RET instruction. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal}. + */ + public void ret(final int local) { + mv.visitVarInsn(Opcodes.RET, local); + } + + /** + * Generates the instructions for a switch statement. + * + * @param keys the switch case keys. + * @param generator a generator to generate the code for the switch cases. + */ + public void tableSwitch( + final int[] keys, + final TableSwitchGenerator generator) + { + float density; + if (keys.length == 0) { + density = 0; + } else { + density = (float) keys.length + / (keys[keys.length - 1] - keys[0] + 1); + } + tableSwitch(keys, generator, density >= 0.5f); + } + + /** + * Generates the instructions for a switch statement. + * + * @param keys the switch case keys. + * @param generator a generator to generate the code for the switch cases. + * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or + * <tt>false</tt> to use a LOOKUPSWITCH instruction. + */ + public void tableSwitch( + final int[] keys, + final TableSwitchGenerator generator, + final boolean useTable) + { + for (int i = 1; i < keys.length; ++i) { + if (keys[i] < keys[i - 1]) { + throw new IllegalArgumentException("keys must be sorted ascending"); + } + } + Label def = newLabel(); + Label end = newLabel(); + if (keys.length > 0) { + int len = keys.length; + int min = keys[0]; + int max = keys[len - 1]; + int range = max - min + 1; + if (useTable) { + Label[] labels = new Label[range]; + Arrays.fill(labels, def); + for (int i = 0; i < len; ++i) { + labels[keys[i] - min] = newLabel(); + } + mv.visitTableSwitchInsn(min, max, def, labels); + for (int i = 0; i < range; ++i) { + Label label = labels[i]; + if (label != def) { + mark(label); + generator.generateCase(i + min, end); + } + } + } else { + Label[] labels = new Label[len]; + for (int i = 0; i < len; ++i) { + labels[i] = newLabel(); + } + mv.visitLookupSwitchInsn(def, keys, labels); + for (int i = 0; i < len; ++i) { + mark(labels[i]); + generator.generateCase(keys[i], end); + } + } + } + mark(def); + generator.generateDefault(); + mark(end); + } + + /** + * Generates the instruction to return the top stack value to the caller. + */ + public void returnValue() { + mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); + } + + // ------------------------------------------------------------------------ + // Instructions to load and store fields + // ------------------------------------------------------------------------ + + /** + * Generates a get field or set field instruction. + * + * @param opcode the instruction's opcode. + * @param ownerType the class in which the field is defined. + * @param name the name of the field. + * @param fieldType the type of the field. + */ + private void fieldInsn( + final int opcode, + final Type ownerType, + final String name, + final Type fieldType) + { + mv.visitFieldInsn(opcode, + ownerType.getInternalName(), + name, + fieldType.getDescriptor()); + } + + /** + * Generates the instruction to push the value of a static field on the + * stack. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ + public void getStatic(final Type owner, final String name, final Type type) + { + fieldInsn(Opcodes.GETSTATIC, owner, name, type); + } + + /** + * Generates the instruction to store the top stack value in a static field. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ + public void putStatic(final Type owner, final String name, final Type type) + { + fieldInsn(Opcodes.PUTSTATIC, owner, name, type); + } + + /** + * Generates the instruction to push the value of a non static field on the + * stack. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ + public void getField(final Type owner, final String name, final Type type) { + fieldInsn(Opcodes.GETFIELD, owner, name, type); + } + + /** + * Generates the instruction to store the top stack value in a non static + * field. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ + public void putField(final Type owner, final String name, final Type type) { + fieldInsn(Opcodes.PUTFIELD, owner, name, type); + } + + // ------------------------------------------------------------------------ + // Instructions to invoke methods + // ------------------------------------------------------------------------ + + /** + * Generates an invoke method instruction. + * + * @param opcode the instruction's opcode. + * @param type the class in which the method is defined. + * @param method the method to be invoked. + */ + private void invokeInsn( + final int opcode, + final Type type, + final Method method) + { + String owner = type.getSort() == Type.ARRAY + ? type.getDescriptor() + : type.getInternalName(); + mv.visitMethodInsn(opcode, + owner, + method.getName(), + method.getDescriptor()); + } + + /** + * Generates the instruction to invoke a normal method. + * + * @param owner the class in which the method is defined. + * @param method the method to be invoked. + */ + public void invokeVirtual(final Type owner, final Method method) { + invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); + } + + /** + * Generates the instruction to invoke a constructor. + * + * @param type the class in which the constructor is defined. + * @param method the constructor to be invoked. + */ + public void invokeConstructor(final Type type, final Method method) { + invokeInsn(Opcodes.INVOKESPECIAL, type, method); + } + + /** + * Generates the instruction to invoke a static method. + * + * @param owner the class in which the method is defined. + * @param method the method to be invoked. + */ + public void invokeStatic(final Type owner, final Method method) { + invokeInsn(Opcodes.INVOKESTATIC, owner, method); + } + + /** + * Generates the instruction to invoke an interface method. + * + * @param owner the class in which the method is defined. + * @param method the method to be invoked. + */ + public void invokeInterface(final Type owner, final Method method) { + invokeInsn(Opcodes.INVOKEINTERFACE, owner, method); + } + + // ------------------------------------------------------------------------ + // Instructions to create objects and arrays + // ------------------------------------------------------------------------ + + /** + * Generates a type dependent instruction. + * + * @param opcode the instruction's opcode. + * @param type the instruction's operand. + */ + private void typeInsn(final int opcode, final Type type) { + String desc; + if (type.getSort() == Type.ARRAY) { + desc = type.getDescriptor(); + } else { + desc = type.getInternalName(); + } + mv.visitTypeInsn(opcode, desc); + } + + /** + * Generates the instruction to create a new object. + * + * @param type the class of the object to be created. + */ + public void newInstance(final Type type) { + typeInsn(Opcodes.NEW, type); + } + + /** + * Generates the instruction to create a new array. + * + * @param type the type of the array elements. + */ + public void newArray(final Type type) { + int typ; + switch (type.getSort()) { + case Type.BOOLEAN: + typ = Opcodes.T_BOOLEAN; + break; + case Type.CHAR: + typ = Opcodes.T_CHAR; + break; + case Type.BYTE: + typ = Opcodes.T_BYTE; + break; + case Type.SHORT: + typ = Opcodes.T_SHORT; + break; + case Type.INT: + typ = Opcodes.T_INT; + break; + case Type.FLOAT: + typ = Opcodes.T_FLOAT; + break; + case Type.LONG: + typ = Opcodes.T_LONG; + break; + case Type.DOUBLE: + typ = Opcodes.T_DOUBLE; + break; + default: + typeInsn(Opcodes.ANEWARRAY, type); + return; + } + mv.visitIntInsn(Opcodes.NEWARRAY, typ); + } + + // ------------------------------------------------------------------------ + // Miscelaneous instructions + // ------------------------------------------------------------------------ + + /** + * Generates the instruction to compute the length of an array. + */ + public void arrayLength() { + mv.visitInsn(Opcodes.ARRAYLENGTH); + } + + /** + * Generates the instruction to throw an exception. + */ + public void throwException() { + mv.visitInsn(Opcodes.ATHROW); + } + + /** + * Generates the instructions to create and throw an exception. The + * exception class must have a constructor with a single String argument. + * + * @param type the class of the exception to be thrown. + * @param msg the detailed message of the exception. + */ + public void throwException(final Type type, final String msg) { + newInstance(type); + dup(); + push(msg); + invokeConstructor(type, Method.getMethod("void <init> (String)")); + throwException(); + } + + /** + * Generates the instruction to check that the top stack value is of the + * given type. + * + * @param type a class or interface type. + */ + public void checkCast(final Type type) { + if (!type.equals(OBJECT_TYPE)) { + typeInsn(Opcodes.CHECKCAST, type); + } + } + + /** + * Generates the instruction to test if the top stack value is of the given + * type. + * + * @param type a class or interface type. + */ + public void instanceOf(final Type type) { + typeInsn(Opcodes.INSTANCEOF, type); + } + + /** + * Generates the instruction to get the monitor of the top stack value. + */ + public void monitorEnter() { + mv.visitInsn(Opcodes.MONITORENTER); + } + + /** + * Generates the instruction to release the monitor of the top stack value. + */ + public void monitorExit() { + mv.visitInsn(Opcodes.MONITOREXIT); + } + + // ------------------------------------------------------------------------ + // Non instructions + // ------------------------------------------------------------------------ + + /** + * Marks the end of the visited method. + */ + public void endMethod() { + if ((access & Opcodes.ACC_ABSTRACT) == 0) { + mv.visitMaxs(0, 0); + } + } + + /** + * Marks the start of an exception handler. + * + * @param start beginning of the exception handler's scope (inclusive). + * @param end end of the exception handler's scope (exclusive). + * @param exception internal name of the type of exceptions handled by the + * handler. + */ + public void catchException( + final Label start, + final Label end, + final Type exception) + { + mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName()); + } +}
diff --git a/asmx/src/org/objectweb/asm/commons/LocalVariablesSorter.java b/asmx/src/org/objectweb/asm/commons/LocalVariablesSorter.java new file mode 100644 index 0000000..1253a0b --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/LocalVariablesSorter.java
@@ -0,0 +1,136 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodAdapter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A {@link MethodAdapter} that renumbers local variables in their order of + * appearance. This adapter allows one to easily add new local variables to a + * method. + * + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class LocalVariablesSorter extends MethodAdapter { + + /** + * Mapping from old to new local variable indexes. A local variable at index + * i of size 1 is remapped to 'mapping[2*i]', while a local variable at + * index i of size 2 is remapped to 'mapping[2*i+1]'. + */ + private int[] mapping = new int[40]; + + protected final int firstLocal; + + private int nextLocal; + + public LocalVariablesSorter( + final int access, + final String desc, + final MethodVisitor mv) + { + super(mv); + Type[] args = Type.getArgumentTypes(desc); + nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1; + for (int i = 0; i < args.length; i++) { + nextLocal += args[i].getSize(); + } + firstLocal = nextLocal; + } + + public void visitVarInsn(final int opcode, final int var) { + int size; + switch (opcode) { + case Opcodes.LLOAD: + case Opcodes.LSTORE: + case Opcodes.DLOAD: + case Opcodes.DSTORE: + size = 2; + break; + default: + size = 1; + } + mv.visitVarInsn(opcode, remap(var, size)); + } + + public void visitIincInsn(final int var, final int increment) { + mv.visitIincInsn(remap(var, 1), increment); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + mv.visitMaxs(maxStack, nextLocal); + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1; + mv.visitLocalVariable(name, desc, signature, start, end, remap(index, size)); + } + + // ------------- + + protected int newLocal(final int size) { + int var = nextLocal; + nextLocal += size; + return var; + } + + private int remap(final int var, final int size) { + if (var < firstLocal) { + return var; + } + int key = 2 * var + size - 1; + int length = mapping.length; + if (key >= length) { + int[] newMapping = new int[Math.max(2 * length, key + 1)]; + System.arraycopy(mapping, 0, newMapping, 0, length); + mapping = newMapping; + } + int value = mapping[key]; + if (value == 0) { + value = nextLocal + 1; + mapping[key] = value; + nextLocal += size; + } + return value - 1; + } + +}
diff --git a/asmx/src/org/objectweb/asm/commons/Method.java b/asmx/src/org/objectweb/asm/commons/Method.java new file mode 100644 index 0000000..741e7b5 --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/Method.java
@@ -0,0 +1,220 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import java.util.HashMap; +import java.util.Map; + +import org.objectweb.asm.Type; + +/** + * A named method descriptor. + * + * @author Juozas Baliuka + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class Method { + + /** + * The method name. + */ + private final String name; + + /** + * The method descriptor. + */ + private final String desc; + + /** + * Maps primitive Java type names to their descriptors. + */ + private final static Map DESCRIPTORS; + + static { + DESCRIPTORS = new HashMap(); + DESCRIPTORS.put("void", "V"); + DESCRIPTORS.put("byte", "B"); + DESCRIPTORS.put("char", "C"); + DESCRIPTORS.put("double", "D"); + DESCRIPTORS.put("float", "F"); + DESCRIPTORS.put("int", "I"); + DESCRIPTORS.put("long", "J"); + DESCRIPTORS.put("short", "S"); + DESCRIPTORS.put("boolean", "Z"); + } + + /** + * Creates a new {@link Method}. + * + * @param name the method's name. + * @param desc the method's descriptor. + */ + public Method(final String name, final String desc) { + this.name = name; + this.desc = desc; + } + + /** + * Creates a new {@link Method}. + * + * @param name the method's name. + * @param returnType the method's return type. + * @param argumentTypes the method's argument types. + */ + public Method( + final String name, + final Type returnType, + final Type[] argumentTypes) + { + this(name, Type.getMethodDescriptor(returnType, argumentTypes)); + } + + /** + * Returns a {@link Method} corresponding to the given Java method + * declaration. + * + * @param method a Java method declaration, without argument names, of the + * form "returnType name (argumentType1, ... argumentTypeN)", where + * the types are in plain Java (e.g. "int", "float", + * "java.util.List", ...). + * @return a {@link Method} corresponding to the given Java method + * declaration. + * @throws IllegalArgumentException if <code>method</code> could not get + * parsed. + */ + public static Method getMethod(final String method) + throws IllegalArgumentException + { + int space = method.indexOf(' '); + int start = method.indexOf('(', space) + 1; + int end = method.indexOf(')', start); + if (space == -1 || start == -1 || end == -1) { + throw new IllegalArgumentException(); + } + // TODO: Check validity of returnType, methodName and arguments. + String returnType = method.substring(0, space); + String methodName = method.substring(space + 1, start - 1).trim(); + StringBuffer sb = new StringBuffer(); + sb.append('('); + int p; + do { + p = method.indexOf(',', start); + if (p == -1) { + sb.append(map(method.substring(start, end).trim())); + } else { + sb.append(map(method.substring(start, p).trim())); + start = p + 1; + } + } while (p != -1); + sb.append(')'); + sb.append(map(returnType)); + return new Method(methodName, sb.toString()); + } + + private static String map(final String type) { + if (type.equals("")) { + return type; + } + + StringBuffer sb = new StringBuffer(); + int index = 0; + while ((index = type.indexOf("[]", index) + 1) > 0) { + sb.append('['); + } + + String t = type.substring(0, type.length() - sb.length() * 2); + String desc = (String) DESCRIPTORS.get(t); + if (desc != null) { + sb.append(desc); + } else { + sb.append('L'); + if (t.indexOf('.') < 0) { + sb.append("java/lang/" + t); + } else { + sb.append(t.replace('.', '/')); + } + sb.append(';'); + } + return sb.toString(); + } + + /** + * Returns the name of the method described by this object. + * + * @return the name of the method described by this object. + */ + public String getName() { + return name; + } + + /** + * Returns the descriptor of the method described by this object. + * + * @return the descriptor of the method described by this object. + */ + public String getDescriptor() { + return desc; + } + + /** + * Returns the return type of the method described by this object. + * + * @return the return type of the method described by this object. + */ + public Type getReturnType() { + return Type.getReturnType(desc); + } + + /** + * Returns the argument types of the method described by this object. + * + * @return the argument types of the method described by this object. + */ + public Type[] getArgumentTypes() { + return Type.getArgumentTypes(desc); + } + + public String toString() { + return name + desc; + } + + public boolean equals(final Object o) { + if (!(o instanceof Method)) { + return false; + } + Method other = (Method) o; + return name.equals(other.name) && desc.equals(other.desc); + } + + public int hashCode() { + return name.hashCode() ^ desc.hashCode(); + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/commons/SerialVersionUIDAdder.java b/asmx/src/org/objectweb/asm/commons/SerialVersionUIDAdder.java new file mode 100644 index 0000000..800ad4b --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/SerialVersionUIDAdder.java
@@ -0,0 +1,490 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A {@link ClassAdapter} that adds a serial version unique identifier to a + * class if missing. Here is typical usage of this class: + * + * <pre> + * ClassWriter cw = new ClassWriter(...); + * ClassVisitor sv = new SerialVersionUIDAdder(cw); + * ClassVisitor ca = new MyClassAdapter(sv); + * new ClassReader(orginalClass).accept(ca, false); + * </pre> + * + * The SVUID algorithm can be found <a href= + * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html" + * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>: + * + * <pre> + * The serialVersionUID is computed using the signature of a stream of bytes + * that reflect the class definition. The National Institute of Standards and + * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a + * signature for the stream. The first two 32-bit quantities are used to form a + * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data + * types to a sequence of bytes. The values input to the stream are defined by + * the Java Virtual Machine (VM) specification for classes. + * + * The sequence of items in the stream is as follows: + * + * 1. The class name written using UTF encoding. + * 2. The class modifiers written as a 32-bit integer. + * 3. The name of each interface sorted by name written using UTF encoding. + * 4. For each field of the class sorted by field name (except private static + * and private transient fields): + * 1. The name of the field in UTF encoding. + * 2. The modifiers of the field written as a 32-bit integer. + * 3. The descriptor of the field in UTF encoding + * 5. If a class initializer exists, write out the following: + * 1. The name of the method, <clinit>, in UTF encoding. + * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, + * written as a 32-bit integer. + * 3. The descriptor of the method, ()V, in UTF encoding. + * 6. For each non-private constructor sorted by method name and signature: + * 1. The name of the method, <init>, in UTF encoding. + * 2. The modifiers of the method written as a 32-bit integer. + * 3. The descriptor of the method in UTF encoding. + * 7. For each non-private method sorted by method name and signature: + * 1. The name of the method in UTF encoding. + * 2. The modifiers of the method written as a 32-bit integer. + * 3. The descriptor of the method in UTF encoding. + * 8. The SHA-1 algorithm is executed on the stream of bytes produced by + * DataOutputStream and produces five 32-bit values sha[0..4]. + * + * 9. The hash value is assembled from the first and second 32-bit values of + * the SHA-1 message digest. If the result of the message digest, the five + * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named + * sha, the hash value would be computed as follows: + * + * long hash = ((sha[0] >>> 24) & 0xFF) | + * ((sha[0] >>> 16) & 0xFF) << 8 | + * ((sha[0] >>> 8) & 0xFF) << 16 | + * ((sha[0] >>> 0) & 0xFF) << 24 | + * ((sha[1] >>> 24) & 0xFF) << 32 | + * ((sha[1] >>> 16) & 0xFF) << 40 | + * ((sha[1] >>> 8) & 0xFF) << 48 | + * ((sha[1] >>> 0) & 0xFF) << 56; + * </pre> + * + * @author Rajendra Inamdar, Vishal Vishnoi + */ +public class SerialVersionUIDAdder extends ClassAdapter { + + /** + * Flag that indicates if we need to compute SVUID. + */ + protected boolean computeSVUID; + + /** + * Set to true if the class already has SVUID. + */ + protected boolean hasSVUID; + + /** + * Classes access flags. + */ + protected int access; + + /** + * Internal name of the class + */ + protected String name; + + /** + * Interfaces implemented by the class. + */ + protected String[] interfaces; + + /** + * Collection of fields. (except private static and private transient + * fields) + */ + protected Collection svuidFields; + + /** + * Set to true if the class has static initializer. + */ + protected boolean hasStaticInitializer; + + /** + * Collection of non-private constructors. + */ + protected Collection svuidConstructors; + + /** + * Collection of non-private methods. + */ + protected Collection svuidMethods; + + /** + * Creates a new {@link SerialVersionUIDAdder}. + * + * @param cv a {@link ClassVisitor} to which this visitor will delegate + * calls. + */ + public SerialVersionUIDAdder(final ClassVisitor cv) { + super(cv); + svuidFields = new ArrayList(); + svuidConstructors = new ArrayList(); + svuidMethods = new ArrayList(); + } + + // ------------------------------------------------------------------------ + // Overriden methods + // ------------------------------------------------------------------------ + + /* + * Visit class header and get class name, access , and intefraces + * informatoin (step 1,2, and 3) for SVUID computation. + */ + 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; + + if (computeSVUID) { + this.name = name; + this.access = access; + this.interfaces = interfaces; + } + + super.visit(version, access, name, signature, superName, interfaces); + } + + /* + * Visit the methods and get constructor and method information (step 5 and + * 7). Also determince if there is a class initializer (step 6). + */ + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + if (computeSVUID) { + if (name.equals("<clinit>")) { + hasStaticInitializer = true; + } + /* + * Remembers non private constructors and methods for SVUID + * computation For constructor and method modifiers, only the + * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, + * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags + * are used. + */ + int mods = access + & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE + | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC + | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED + | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT); + + // all non private methods + if ((access & Opcodes.ACC_PRIVATE) == 0) { + if (name.equals("<init>")) { + svuidConstructors.add(new Item(name, mods, desc)); + } else if (!name.equals("<clinit>")) { + svuidMethods.add(new Item(name, mods, desc)); + } + } + } + + return cv.visitMethod(access, name, desc, signature, exceptions); + } + + /* + * Gets class field information for step 4 of the alogrithm. Also determines + * if the class already has a SVUID. + */ + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + if (computeSVUID) { + if (name.equals("serialVersionUID")) { + // since the class already has SVUID, we won't be computing it. + computeSVUID = false; + hasSVUID = true; + } + /* + * Remember field for SVUID computation For field modifiers, only + * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, + * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when + * computing serialVersionUID values. + */ + int mods = access + & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE + | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC + | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT); + + if (((access & Opcodes.ACC_PRIVATE) == 0) + || ((access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0)) + { + svuidFields.add(new Item(name, mods, desc)); + } + } + + return super.visitField(access, name, desc, signature, value); + } + + /* + * Add the SVUID if class doesn't have one + */ + public void visitEnd() { + // compute SVUID and add it to the class + if (computeSVUID && !hasSVUID) { + try { + cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, + "serialVersionUID", + "J", + null, + new Long(computeSVUID())); + } catch (Throwable e) { + throw new RuntimeException("Error while computing SVUID for " + + name, e); + } + } + + super.visitEnd(); + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Returns the value of SVUID if the class doesn't have one already. Please + * note that 0 is returned if the class already has SVUID, thus use + * <code>isHasSVUID</code> to determine if the class already had an SVUID. + * + * @return Returns the serial version UID + * @throws IOException + */ + protected long computeSVUID() throws IOException { + if (hasSVUID) { + return 0; + } + + ByteArrayOutputStream bos = null; + DataOutputStream dos = null; + long svuid = 0; + + try { + bos = new ByteArrayOutputStream(); + dos = new DataOutputStream(bos); + + /* + * 1. The class name written using UTF encoding. + */ + dos.writeUTF(name.replace('/', '.')); + + /* + * 2. The class modifiers written as a 32-bit integer. + */ + dos.writeInt(access + & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL + | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)); + + /* + * 3. The name of each interface sorted by name written using UTF + * encoding. + */ + Arrays.sort(interfaces); + for (int i = 0; i < interfaces.length; i++) { + dos.writeUTF(interfaces[i].replace('/', '.')); + } + + /* + * 4. For each field of the class sorted by field name (except + * private static and private transient fields): + * + * 1. The name of the field in UTF encoding. 2. The modifiers of the + * field written as a 32-bit integer. 3. The descriptor of the field + * in UTF encoding + * + * Note that field signatutes are not dot separated. Method and + * constructor signatures are dot separated. Go figure... + */ + writeItems(svuidFields, dos, false); + + /* + * 5. If a class initializer exists, write out the following: 1. The + * name of the method, <clinit>, in UTF encoding. 2. The modifier of + * the method, java.lang.reflect.Modifier.STATIC, written as a + * 32-bit integer. 3. The descriptor of the method, ()V, in UTF + * encoding. + */ + if (hasStaticInitializer) { + dos.writeUTF("<clinit>"); + dos.writeInt(Opcodes.ACC_STATIC); + dos.writeUTF("()V"); + } // if.. + + /* + * 6. For each non-private constructor sorted by method name and + * signature: 1. The name of the method, <init>, in UTF encoding. 2. + * The modifiers of the method written as a 32-bit integer. 3. The + * descriptor of the method in UTF encoding. + */ + writeItems(svuidConstructors, dos, true); + + /* + * 7. For each non-private method sorted by method name and + * signature: 1. The name of the method in UTF encoding. 2. The + * modifiers of the method written as a 32-bit integer. 3. The + * descriptor of the method in UTF encoding. + */ + writeItems(svuidMethods, dos, true); + + dos.flush(); + + /* + * 8. The SHA-1 algorithm is executed on the stream of bytes + * produced by DataOutputStream and produces five 32-bit values + * sha[0..4]. + */ + byte[] hashBytes = computeSHAdigest(bos.toByteArray()); + + /* + * 9. The hash value is assembled from the first and second 32-bit + * values of the SHA-1 message digest. If the result of the message + * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of + * five int values named sha, the hash value would be computed as + * follows: + * + * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) << + * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) << + * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) << + * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) << + * 56; + */ + for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) { + svuid = (svuid << 8) | (hashBytes[i] & 0xFF); + } + } finally { + // close the stream (if open) + if (dos != null) { + dos.close(); + } + } + + return svuid; + } + + /** + * Returns the SHA-1 message digest of the given value. + * + * @param value the value whose SHA message digest must be computed. + * @return the SHA-1 message digest of the given value. + */ + protected byte[] computeSHAdigest(byte[] value) { + try { + return MessageDigest.getInstance("SHA").digest(value); + } catch (Exception e) { + throw new UnsupportedOperationException(e); + } + } + + /** + * Sorts the items in the collection and writes it to the data output stream + * + * @param itemCollection collection of items + * @param dos a <code>DataOutputStream</code> value + * @param dotted a <code>boolean</code> value + * @exception IOException if an error occurs + */ + private void writeItems( + final Collection itemCollection, + final DataOutputStream dos, + final boolean dotted) throws IOException + { + int size = itemCollection.size(); + Item items[] = (Item[]) itemCollection.toArray(new Item[size]); + Arrays.sort(items); + for (int i = 0; i < size; i++) { + dos.writeUTF(items[i].name); + dos.writeInt(items[i].access); + dos.writeUTF(dotted + ? items[i].desc.replace('/', '.') + : items[i].desc); + } + } + + // ------------------------------------------------------------------------ + // Inner classes + // ------------------------------------------------------------------------ + + static class Item implements Comparable { + + String name; + + int access; + + String desc; + + Item(final String name, final int access, final String desc) { + this.name = name; + this.access = access; + this.desc = desc; + } + + public int compareTo(final Object o) { + Item other = (Item) o; + int retVal = name.compareTo(other.name); + if (retVal == 0) { + retVal = desc.compareTo(other.desc); + } + return retVal; + } + } +}
diff --git a/asmx/src/org/objectweb/asm/commons/StaticInitMerger.java b/asmx/src/org/objectweb/asm/commons/StaticInitMerger.java new file mode 100644 index 0000000..9aabe44 --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/StaticInitMerger.java
@@ -0,0 +1,99 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A {@link ClassAdapter} that merges clinit methods into a single one. + * + * @author Eric Bruneton + */ +public class StaticInitMerger extends ClassAdapter { + + private String name; + + private MethodVisitor clinit; + + private String prefix; + + private int counter; + + public StaticInitMerger(final String prefix, final ClassVisitor cv) { + super(cv); + this.prefix = prefix; + } + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + cv.visit(version, access, name, signature, superName, interfaces); + this.name = name; + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + MethodVisitor mv; + if (name.equals("<clinit>")) { + int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC; + String n = prefix + counter++; + mv = cv.visitMethod(a, n, desc, signature, exceptions); + + if (clinit == null) { + clinit = cv.visitMethod(a, name, desc, null, null); + } + clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc); + } else { + mv = cv.visitMethod(access, name, desc, signature, exceptions); + } + return mv; + } + + public void visitEnd() { + if (clinit != null) { + clinit.visitInsn(Opcodes.RETURN); + clinit.visitMaxs(0, 0); + } + cv.visitEnd(); + } +}
diff --git a/asmx/src/org/objectweb/asm/commons/TableSwitchGenerator.java b/asmx/src/org/objectweb/asm/commons/TableSwitchGenerator.java new file mode 100644 index 0000000..73d8d92 --- /dev/null +++ b/asmx/src/org/objectweb/asm/commons/TableSwitchGenerator.java
@@ -0,0 +1,55 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.commons; + +import org.objectweb.asm.Label; + +/** + * A code generator for switch statements. + * + * @author Juozas Baliuka + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public interface TableSwitchGenerator { + + /** + * Generates the code for a switch case. + * + * @param key the switch case key. + * @param end a label that corresponds to the end of the switch statement. + */ + void generateCase(int key, Label end); + + /** + * Generates the code for the default switch case. + */ + void generateDefault(); +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/AnnotationConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/AnnotationConstantsCollector.java new file mode 100644 index 0000000..70b3f14 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/AnnotationConstantsCollector.java
@@ -0,0 +1,155 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Type; + +/** + * An {@link AnnotationVisitor} that collects the {@link Constant}s of the + * annotations it visits. + * + * @author Eric Bruneton + */ +public class AnnotationConstantsCollector implements AnnotationVisitor { + + private final AnnotationVisitor av; + + private final ConstantPool cp; + + public AnnotationConstantsCollector( + final AnnotationVisitor av, + final ConstantPool cp) + { + this.av = 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/asmx/src/org/objectweb/asm/optimizer/ClassConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/ClassConstantsCollector.java new file mode 100644 index 0000000..9cfc6de --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/ClassConstantsCollector.java
@@ -0,0 +1,212 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A {@link ClassVisitor} that collects the {@link Constant}s of the classes it + * visits. + * + * @author Eric Bruneton + */ +public class ClassConstantsCollector extends ClassAdapter { + + private ConstantPool cp; + + public ClassConstantsCollector(final ClassVisitor cv, final ConstantPool cp) + { + super(cv); + this.cp = cp; + } + + 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); + } + + 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); + } + + 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); + } + + 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); + } + + public void visitAttribute(final Attribute attr) { + // can do nothing + cv.visitAttribute(attr); + } + + 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.newClass(innerName); + } + cv.visitInnerClass(name, outerName, innerName, access); + } + + 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); + } + + 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); + } + + public void visitEnd() { + cv.visitEnd(); + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/ClassOptimizer.java b/asmx/src/org/objectweb/asm/optimizer/ClassOptimizer.java new file mode 100644 index 0000000..153b07a --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/ClassOptimizer.java
@@ -0,0 +1,182 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A {@link ClassAdapter} that renames fields and methods, and removes debug + * info. + * + * @author Eric Bruneton + */ +public class ClassOptimizer extends ClassAdapter { + + private NameMapping mapping; + + private String className; + + private String pkgName; + + public ClassOptimizer(final ClassVisitor cv, final NameMapping mapping) { + super(cv); + this.mapping = mapping; + } + + public String getClassName() { + return className; + } + + // ------------------------------------------------------------------------ + // Overriden methods + // ------------------------------------------------------------------------ + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + className = name; + pkgName = name.substring(0, name.lastIndexOf('/')); + cv.visit(version, + access, + mapping.map(name), + null, + mapping.map(superName), + interfaces); + } + + public void visitSource(final String source, final String debug) { + // remove debug info + } + + public void visitOuterClass( + final String owner, + final String name, + final String desc) + { + // remove debug info + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + throw new UnsupportedOperationException(); + } + + public void visitAttribute(final Attribute attr) { + // remove non standard attribute + } + + public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access) + { + // remove debug info + } + + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + String s = className + "." + name; + if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) { + if ((access & Opcodes.ACC_FINAL) != 0 + && (access & Opcodes.ACC_STATIC) != 0 && desc.equals("I")) + { + return null; + } + if (pkgName.equals("org/objectweb/asm") + && mapping.map(s).equals(name)) + { + System.out.println("INFO: " + s + " could be renamed"); + } + cv.visitField(access, + mapping.map(s), + mapping.fix(desc), + null, + value); + } else { + if (!mapping.map(s).equals(name)) { + throw new RuntimeException("The public or protected field " + s + + " must not be renamed."); + } + cv.visitField(access, name, desc, null, value); + } + return null; // remove debug info + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + String s = className + "." + name + desc; + if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) { + if (pkgName.equals("org/objectweb/asm") && !name.startsWith("<") + && mapping.map(s).equals(name)) + { + System.out.println("INFO: " + s + " could be renamed"); + } + return new MethodOptimizer(cv.visitMethod(access, + mapping.map(s), + mapping.fix(desc), + null, + exceptions), mapping); + } else { + if (!mapping.map(s).equals(name)) { + throw new RuntimeException("The public or protected method " + + s + " must not be renamed."); + } + return new MethodOptimizer(cv.visitMethod(access, + name, + desc, + null, + exceptions), mapping); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/Constant.java b/asmx/src/org/objectweb/asm/optimizer/Constant.java new file mode 100644 index 0000000..b07b7c2 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/Constant.java
@@ -0,0 +1,265 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.ClassWriter; + +/** + * 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, or N + * (for Constant Integer, Long, Float, Double, STR, UTF8, Class, NameType, + * Fieldref, Methodref, or InterfaceMethodref constant pool items + * respectively). + */ + 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. + */ + String strVal3; + + /** + * The hash code value of this constant pool item. + */ + int hashCode; + + public Constant() { + } + + public 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; + strVal3 = i.strVal3; + 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.strVal3 = strVal3; + switch (type) { + case 's': + case 'S': + case 'C': + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); + return; + case 'T': + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode()); + return; + // case 'G': + // case 'M': + // case 'N': + default: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode() * strVal3.hashCode()); + } + } + + void write(final ClassWriter cw) { + switch (type) { + case 'I': + cw.newConst(new Integer(intVal)); + break; + case 'J': + cw.newConst(new Long(longVal)); + break; + case 'F': + cw.newConst(new Float(floatVal)); + break; + case 'D': + cw.newConst(new Double(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, strVal3); + break; + case 'M': + cw.newMethod(strVal1, strVal2, strVal3, false); + break; + case 'N': + cw.newMethod(strVal1, strVal2, strVal3, true); + break; + } + } + + 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 c.floatVal == floatVal; + case 'D': + return c.doubleVal == doubleVal; + case 's': + case 'S': + case 'C': + return c.strVal1.equals(strVal1); + case 'T': + return c.strVal1.equals(strVal1) + && c.strVal2.equals(strVal2); + // case 'G': + // case 'M': + // case 'N': + default: + return c.strVal1.equals(strVal1) + && c.strVal2.equals(strVal2) + && c.strVal3.equals(strVal3); + } + } + return false; + } + + public int hashCode() { + return hashCode; + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/ConstantPool.java b/asmx/src/org/objectweb/asm/optimizer/ConstantPool.java new file mode 100644 index 0000000..c918bef --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/ConstantPool.java
@@ -0,0 +1,198 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import java.util.HashMap; + +import org.objectweb.asm.Type; + +/** + * A constant pool. + * + * @author Eric Bruneton + */ +public class ConstantPool extends HashMap { + + private Constant key1 = new Constant(); + + private Constant key2 = new Constant(); + + private Constant key3 = 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 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; + return newClass(t.getSort() == Type.OBJECT + ? t.getInternalName() + : t.getDescriptor()); + } 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 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 (Constant) get((Object) key); + } + + private void put(final Constant cst) { + put(cst, cst); + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/optimizer/FieldConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/FieldConstantsCollector.java new file mode 100644 index 0000000..fa86e44 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/FieldConstantsCollector.java
@@ -0,0 +1,92 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; + +/** + * A {@link FieldVisitor} that collects the {@link Constant}s of the fields it + * visits. + * + * @author Eric Bruneton + */ +public class FieldConstantsCollector implements FieldVisitor { + + private FieldVisitor fv; + + private ConstantPool cp; + + public FieldConstantsCollector(final FieldVisitor fv, final ConstantPool cp) + { + this.fv = fv; + this.cp = cp; + } + + 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); + } + + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + cp.newUTF8(desc); + if(visible) { + cp.newUTF8("RuntimeVisibleTypeAnnotations"); + } else { + cp.newUTF8("RuntimeInvisibleTypeAnnotations"); + } + return new TypeAnnotationConstantsCollector( + fv.visitTypeAnnotation(desc, visible, inCode), cp); + } + + public void visitAttribute(final Attribute attr) { + // can do nothing + fv.visitAttribute(attr); + } + + public void visitEnd() { + fv.visitEnd(); + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/JarOptimizer.java b/asmx/src/org/objectweb/asm/optimizer/JarOptimizer.java new file mode 100644 index 0000000..8302d99 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/JarOptimizer.java
@@ -0,0 +1,87 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; + +/** + * A Jar file optimizer. + * + * @author Eric Bruneton + */ +public class JarOptimizer { + + public static void main(final String[] args) throws IOException { + File f = new File(args[0]); + optimize(f); + } + + static void optimize(final File f) throws IOException { + 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 = (ZipEntry) e.nextElement(); + if (ze.isDirectory()) { + continue; + } + out.putNextEntry(ze); + 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(); + f.delete(); + g.renameTo(f); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/MethodConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/MethodConstantsCollector.java new file mode 100644 index 0000000..dc7b1c1 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/MethodConstantsCollector.java
@@ -0,0 +1,168 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodAdapter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * An {@link MethodVisitor} that collects the {@link Constant}s of the methods + * it visits. + * + * @author Eric Bruneton + */ +public class MethodConstantsCollector extends MethodAdapter { + + private ConstantPool cp; + + public MethodConstantsCollector( + final MethodVisitor mv, + final ConstantPool cp) + { + super(mv); + this.cp = cp; + } + + public AnnotationVisitor visitAnnotationDefault() { + cp.newUTF8("AnnotationDefault"); + return new AnnotationConstantsCollector(mv.visitAnnotationDefault(), cp); + } + + 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); + } + + 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); + } + + public void visitTypeInsn(final int opcode, final String desc) { + cp.newClass(desc); + mv.visitTypeInsn(opcode, desc); + } + + 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); + } + + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + cp.newMethod(owner, name, desc, itf); + mv.visitMethodInsn(opcode, owner, name, desc); + } + + public void visitLdcInsn(final Object cst) { + cp.newConst(cst); + mv.visitLdcInsn(cst); + } + + public void visitMultiANewArrayInsn(final String desc, final int dims) { + cp.newClass(desc); + mv.visitMultiANewArrayInsn(desc, dims); + } + + 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); + } + + 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); + } + + public void visitLineNumber(final int line, final Label start) { + cp.newUTF8("LineNumberTable"); + mv.visitLineNumber(line, start); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + cp.newUTF8("Code"); + mv.visitMaxs(maxStack, maxLocals); + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/MethodOptimizer.java b/asmx/src/org/objectweb/asm/optimizer/MethodOptimizer.java new file mode 100644 index 0000000..91fa98b --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/MethodOptimizer.java
@@ -0,0 +1,108 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodAdapter; +import org.objectweb.asm.MethodVisitor; + +/** + * A {@link MethodAdapter} that renames fields and methods, and removes debug + * info. + * + * @author Eric Bruneton + */ +public class MethodOptimizer extends MethodAdapter { + + private NameMapping mapping; + + public MethodOptimizer(final MethodVisitor mv, final NameMapping mapping) { + super(mv); + this.mapping = mapping; + } + + // ------------------------------------------------------------------------ + // Overriden methods + // ------------------------------------------------------------------------ + + public AnnotationVisitor visitAnnotationDefault() { + throw new UnsupportedOperationException(); + } + + public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible) + { + throw new UnsupportedOperationException(); + } + + public void visitTypeInsn(final int opcode, final String desc) { + mv.visitTypeInsn(opcode, desc.startsWith("[") + ? mapping.fix(desc) + : mapping.map(desc)); + } + + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + mv.visitFieldInsn(opcode, mapping.map(owner), mapping.map(owner + "." + + name), mapping.fix(desc)); + } + + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + mv.visitMethodInsn(opcode, mapping.map(owner), mapping.map(owner + "." + + name + desc), mapping.fix(desc)); + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + // remove debug info + } + + public void visitLineNumber(final int line, final Label start) { + // remove debug info + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/NameMapping.java b/asmx/src/org/objectweb/asm/optimizer/NameMapping.java new file mode 100644 index 0000000..9cefb1f --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/NameMapping.java
@@ -0,0 +1,101 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; + +import org.objectweb.asm.Type; + +/** + * A mapping from names to names, used to rename classes, fields and methods. + * + * @author Eric Bruneton + */ +public class NameMapping extends Properties { + + public final Set unused; + + public NameMapping(final String file) throws IOException { + load(new FileInputStream(file)); + unused = new HashSet(keySet()); + } + + public String map(final String name) { + String s = (String) get(name); + if (s == null) { + int p = name.indexOf('.'); + if (p != -1) { + int q = name.indexOf('('); + if (q != -1) { + s = name.substring(p + 1, q); + } else { + s = name.substring(p + 1); + } + } else { + s = name; + } + } 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.getType("L" + 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/asmx/src/org/objectweb/asm/optimizer/Shrinker.java b/asmx/src/org/objectweb/asm/optimizer/Shrinker.java new file mode 100644 index 0000000..94e4068 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/Shrinker.java
@@ -0,0 +1,168 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.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.Iterator; +import java.util.Set; +import java.util.TreeSet; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +/** + * A class file shrinker utility. + * + * @author Eric Bruneton + */ +public class Shrinker { + + public static void main(final String[] args) throws IOException { + NameMapping mapping = new NameMapping(args[0]); + File f = new File(args[1]); + File d = new File(args[2]); + optimize(f, d, mapping); + Iterator i = mapping.unused.iterator(); + while (i.hasNext()) { + System.out.println("INFO: unused mapping " + i.next()); + } + } + + static void optimize(final File f, final File d, final NameMapping mapping) + throws IOException + { + if (f.isDirectory()) { + File[] files = f.listFiles(); + for (int i = 0; i < files.length; ++i) { + optimize(files[i], d, mapping); + } + } else if (f.getName().endsWith(".class")) { + ConstantPool cp = new ConstantPool(); + ClassReader cr = new ClassReader(new FileInputStream(f)); + ClassWriter cw = new ClassWriter(false); + ClassConstantsCollector ccc = new ClassConstantsCollector(cw, cp); + ClassOptimizer co = new ClassOptimizer(ccc, mapping); + cr.accept(co, true); + + Set constants = new TreeSet(new ConstantComparator()); + constants.addAll(cp.values()); + + cr = new ClassReader(cw.toByteArray()); + cw = new ClassWriter(false); + Iterator i = constants.iterator(); + while (i.hasNext()) { + Constant c = (Constant) i.next(); + c.write(cw); + } + cr.accept(cw, true); + + String n = mapping.map(co.getClassName()); + File g = new File(d, n + ".class"); + if (!g.exists() || g.lastModified() < f.lastModified()) { + g.getParentFile().mkdirs(); + OutputStream os = new FileOutputStream(g); + os.write(cw.toByteArray()); + os.close(); + } + } + } + + static class ConstantComparator implements Comparator { + + public int compare(final Object o1, final Object o2) { + Constant c1 = (Constant) o1; + Constant c2 = (Constant) o2; + int d = getSort(c1) - getSort(c2); + if (d == 0) { + switch (c1.type) { + case 'I': + return new Integer(c1.intVal).compareTo(new Integer(c2.intVal)); + case 'J': + return new Long(c1.longVal).compareTo(new Long(c2.longVal)); + case 'F': + return new Float(c1.floatVal).compareTo(new Float(c2.floatVal)); + case 'D': + return new Double(c1.doubleVal).compareTo(new Double(c2.doubleVal)); + case 's': + case 'S': + case 'C': + return c1.strVal1.compareTo(c2.strVal1); + case 'T': + d = c1.strVal1.compareTo(c2.strVal1); + if (d == 0) { + d = c1.strVal2.compareTo(c2.strVal2); + } + break; + default: + d = c1.strVal1.compareTo(c2.strVal1); + if (d == 0) { + d = c1.strVal2.compareTo(c2.strVal2); + if (d == 0) { + d = c1.strVal3.compareTo(c2.strVal3); + } + } + } + } + return d; + } + + private int getSort(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; + default: + return 10; + } + } + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/TypeAnnotationConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/TypeAnnotationConstantsCollector.java new file mode 100644 index 0000000..13a6d66 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/TypeAnnotationConstantsCollector.java
@@ -0,0 +1,107 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.optimizer; + +import org.objectweb.asm.TypeAnnotationVisitor; + +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +/** + * An {@link TypeAnnotationVisitor} that collects the + * {@link Constant}s of the extended annotations it visits. + * + * @author jaimeq + */ +public class TypeAnnotationConstantsCollector + extends AnnotationConstantsCollector + implements TypeAnnotationVisitor { + + private TypeAnnotationVisitor xav; + + public TypeAnnotationConstantsCollector( + final TypeAnnotationVisitor xav, + final ConstantPool cp) + { + super(xav, cp); + this.xav = xav; + } + + public void visitXTargetType(int target_type) { + xav.visitXTargetType(target_type); + } + + public void visitXOffset(int offset) { + xav.visitXOffset(offset); + } + + public void visitXLocationLength(int location_length) { + xav.visitXLocationLength(location_length); + } + + public void visitXLocation(TypePathEntry location) { + xav.visitXLocation(location); + } + + public void visitXNumEntries(int num_entries) { + xav.visitXNumEntries(num_entries); + } + + public void visitXStartPc(int start_pc) { + xav.visitXStartPc(start_pc); + } + + public void visitXLength(int length) { + xav.visitXLength(length); + } + + public void visitXIndex(int index) { + xav.visitXIndex(index); + } + + public void visitXParamIndex(int param_index) { + xav.visitXParamIndex(param_index); + } + + public void visitXBoundIndex(int bound_index) { + xav.visitXBoundIndex(bound_index); + } + + public void visitXTypeIndex(int type_index) { + xav.visitXTypeIndex(type_index); + } + + public void visitXExceptionIndex(int exception_index) { + xav.visitXExceptionIndex(exception_index); + } + + public void visitXNameAndArgsSize() { + xav.visitXNameAndArgsSize(); + } +}
diff --git a/asmx/src/org/objectweb/asm/optimizer/shrink.properties b/asmx/src/org/objectweb/asm/optimizer/shrink.properties new file mode 100644 index 0000000..e076a04 --- /dev/null +++ b/asmx/src/org/objectweb/asm/optimizer/shrink.properties
@@ -0,0 +1,279 @@ +# 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 + +# 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/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.access=j +org/objectweb/asm/ClassWriter.name=k +org/objectweb/asm/ClassWriter.signature=l +org/objectweb/asm/ClassWriter.superName=m +org/objectweb/asm/ClassWriter.interfaceCount=n +org/objectweb/asm/ClassWriter.interfaces=o +org/objectweb/asm/ClassWriter.sourceFile=p +org/objectweb/asm/ClassWriter.sourceDebug=q +org/objectweb/asm/ClassWriter.enclosingMethodOwner=r +org/objectweb/asm/ClassWriter.enclosingMethod=s +org/objectweb/asm/ClassWriter.anns=t +org/objectweb/asm/ClassWriter.ianns=u +org/objectweb/asm/ClassWriter.attrs=v +org/objectweb/asm/ClassWriter.innerClassesCount=w +org/objectweb/asm/ClassWriter.innerClasses=x +org/objectweb/asm/ClassWriter.firstField=y +org/objectweb/asm/ClassWriter.lastField=z +org/objectweb/asm/ClassWriter.firstMethod=A +org/objectweb/asm/ClassWriter.lastMethod=B +org/objectweb/asm/ClassWriter.computeMaxs=C +org/objectweb/asm/ClassWriter.typeCount=D +org/objectweb/asm/ClassWriter.typeTable=E +org/objectweb/asm/ClassWriter.thisName=F +org/objectweb/asm/ClassWriter.computeFrames=G +org/objectweb/asm/ClassWriter.computeMaxs=H +org/objectweb/asm/ClassWriter.invalidFrames=I +org/objectweb/asm/ClassWriter.cr=J + +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.next=a +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.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/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.next=a +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.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.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.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.cattrs=J +org/objectweb/asm/MethodWriter.resize=K +org/objectweb/asm/MethodWriter.jsr=L +org/objectweb/asm/MethodWriter.compute=M +org/objectweb/asm/MethodWriter.labels=N +org/objectweb/asm/MethodWriter.previousBlock=O +org/objectweb/asm/MethodWriter.currentBlock=P +org/objectweb/asm/MethodWriter.stackSize=Q +org/objectweb/asm/MethodWriter.maxStackSize=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/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;Lorg/objectweb/asm/ByteVector;)V=a +org/objectweb/asm/AnnotationWriter.put(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/ClassReader.copyPool(Lorg/objectweb/asm/ClassWriter;)V=a +org/objectweb/asm/ClassReader.readAnnotationValue(I[CLjava/lang/String;Lorg/objectweb/asm/AnnotationVisitor;)I=a +org/objectweb/asm/ClassReader.readAnnotationValues(I[CLorg/objectweb/asm/AnnotationVisitor;)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;)[B=a +org/objectweb/asm/ClassReader.readParameterAnnotations(I[CZLorg/objectweb/asm/MethodVisitor;)V=a +org/objectweb/asm/ClassReader.readUTF(II[C)Ljava/lang/String;=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.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/FieldWriter.getSize()I=a +org/objectweb/asm/FieldWriter.put(Lorg/objectweb/asm/ByteVector;)V=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/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/Frame.get(I)I=a +org/objectweb/asm/Frame.set(II)V=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/Frame.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/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.getArgumentsAndReturnSizes(Ljava/lang/String;)I=a +org/objectweb/asm/MethodWriter.getNewOffset([I[III)I=a +org/objectweb/asm/MethodWriter.getSize()I=a +org/objectweb/asm/MethodWriter.put(Lorg/objectweb/asm/ByteVector;)V=a +org/objectweb/asm/MethodWriter.readInt([BI)I=a +org/objectweb/asm/MethodWriter.readShort([BI)S=b +org/objectweb/asm/MethodWriter.readUnsignedShort([BI)I=c +org/objectweb/asm/MethodWriter.writeShort([BII)V=a +org/objectweb/asm/MethodWriter.visitFrame(Lorg/objectweb/asm/Frame;)V=b +org/objectweb/asm/MethodWriter.startFrame(III)V=a +org/objectweb/asm/MethodWriter.endFrame()V=b +org/objectweb/asm/MethodWriter.writeFrame()V=c +org/objectweb/asm/MethodWriter.resizeInstructions()V=d +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/MethodWriter.getNewOffset([I[ILorg/objectweb/asm/Label;)V=a +org/objectweb/asm/MethodWriter.findSubroutine(Lorg/objectweb/asm/Label;I)V=a +org/objectweb/asm/MethodWriter.findSubroutineSuccessors(I[Lorg/objectweb/asm/Label;I)V=a + +org/objectweb/asm/Type.getType([CI)Lorg/objectweb/asm/Type;=a +org/objectweb/asm/Type.getDescriptor(Ljava/lang/StringBuffer;)V=a +org/objectweb/asm/Type.getDescriptor(Ljava/lang/StringBuffer;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/asmx/src/org/objectweb/asm/signature/SignatureReader.java b/asmx/src/org/objectweb/asm/signature/SignatureReader.java new file mode 100644 index 0000000..363692d --- /dev/null +++ b/asmx/src/org/objectweb/asm/signature/SignatureReader.java
@@ -0,0 +1,233 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.signature; + +/** + * A type signature parser to make a signature visitor visit an existing + * signature. + * + * @author Thomas Hallgren + * @author Eric Bruneton + */ +public class SignatureReader { + + /** + * The signature to be read. + */ + private final String signature; + + /** + * Constructs a {@link SignatureReader} for the given signature. + * + * @param signature A <i>ClassSignature</i>, <i>MethodTypeSignature</i>, + * or <i>FieldTypeSignature</i>. + */ + public SignatureReader(final String signature) { + this.signature = signature; + } + + /** + * Makes the given visitor visit the signature of this + * {@link SignatureReader}. This signature is the one specified in the + * constructor (see {@link #SignatureReader(String) SignatureReader}). This + * method is intended to be called on a {@link SignatureReader} that was + * created using a <i>ClassSignature</i> (such as the + * <code>signature</code> parameter of the + * {@link org.objectweb.asm.ClassVisitor#visit ClassVisitor.visit} method) + * or a <i>MethodTypeSignature</i> (such as the <code>signature</code> + * parameter of the + * {@link org.objectweb.asm.ClassVisitor#visitMethod ClassVisitor.visitMethod} + * method). + * + * @param v the visitor that must visit this signature. + */ + public void accept(final SignatureVisitor v) { + String signature = this.signature; + int len = signature.length(); + int pos; + char c; + + if (signature.charAt(0) == '<') { + pos = 2; + do { + int end = signature.indexOf(':', pos); + v.visitFormalTypeParameter(signature.substring(pos - 1, end)); + pos = end + 1; + + c = signature.charAt(pos); + if (c == 'L' || c == '[' || c == 'T') { + pos = parseType(signature, pos, v.visitClassBound()); + } + + for (;;) { + if ((c = signature.charAt(pos++)) == ':') { + pos = parseType(signature, pos, v.visitInterfaceBound()); + } else { + break; + } + } + } while (c != '>'); + } else { + pos = 0; + } + + if (signature.charAt(pos) == '(') { + pos = pos + 1; + while (signature.charAt(pos) != ')') { + pos = parseType(signature, pos, v.visitParameterType()); + } + pos = parseType(signature, pos + 1, v.visitReturnType()); + while (pos < len) { + pos = parseType(signature, pos + 1, v.visitExceptionType()); + } + } else { + pos = parseType(signature, pos, v.visitSuperclass()); + while (pos < len) { + pos = parseType(signature, pos, v.visitInterface()); + } + } + } + + /** + * Makes the given visitor visit the signature of this + * {@link SignatureReader}. This signature is the one specified in the + * constructor (see {@link #SignatureReader(String) SignatureReader}). This + * method is intended to be called on a {@link SignatureReader} that was + * created using a <i>FieldTypeSignature</i>, such as the + * <code>signature</code> parameter of the + * {@link org.objectweb.asm.ClassVisitor#visitField + * ClassVisitor.visitField} or {@link + * org.objectweb.asm.MethodVisitor#visitLocalVariable + * MethodVisitor.visitLocalVariable} methods. + * + * @param v the visitor that must visit this signature. + */ + public void acceptType(final SignatureVisitor v) { + parseType(this.signature, 0, v); + } + + /** + * Parses a field type signature and makes the given visitor visit it. + * + * @param signature a string containing the signature that must be parsed. + * @param pos index of the first character of the signature to parsed. + * @param v the visitor that must visit this signature. + * @return the index of the first character after the parsed signature. + */ + private static int parseType( + final String signature, + int pos, + final SignatureVisitor v) + { + char c; + int start, end; + boolean visited, inner; + String name; + + switch (c = signature.charAt(pos++)) { + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + case 'F': + case 'J': + case 'D': + case 'V': + v.visitBaseType(c); + return pos; + + case '[': + return parseType(signature, pos, v.visitArrayType()); + + case 'T': + end = signature.indexOf(';', pos); + v.visitTypeVariable(signature.substring(pos, end)); + return end + 1; + + default: // case 'L': + start = pos; + visited = false; + inner = false; + for (;;) { + switch (c = signature.charAt(pos++)) { + case '.': + case ';': + if (!visited) { + name = signature.substring(start, pos - 1); + if (inner) { + v.visitInnerClassType(name); + } else { + v.visitClassType(name); + } + } + if (c == ';') { + v.visitEnd(); + return pos; + } + start = pos; + visited = false; + inner = true; + break; + + case '<': + name = signature.substring(start, pos - 1); + if (inner) { + v.visitInnerClassType(name); + } else { + v.visitClassType(name); + } + visited = true; + top: for (;;) { + switch (c = signature.charAt(pos)) { + case '>': + break top; + case '*': + ++pos; + v.visitTypeArgument(); + break; + case '+': + case '-': + pos = parseType(signature, + pos + 1, + v.visitTypeArgument(c)); + break; + default: + pos = parseType(signature, + pos, + v.visitTypeArgument('=')); + break; + } + } + } + } + } + } +}
diff --git a/asmx/src/org/objectweb/asm/signature/SignatureVisitor.java b/asmx/src/org/objectweb/asm/signature/SignatureVisitor.java new file mode 100644 index 0000000..8f087bd --- /dev/null +++ b/asmx/src/org/objectweb/asm/signature/SignatureVisitor.java
@@ -0,0 +1,185 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.signature; + +/** + * A visitor to visit a generic signature. The methods of this interface must be + * called in one of the three following orders (the last one is the only valid + * order for a {@link SignatureVisitor} that is returned by a method of this + * interface): <ul> <li><i>ClassSignature</i> = ( + * <tt>visitFormalTypeParameter</tt> + * <tt>visitClassBound</tt>? + * <tt>visitInterfaceBound</tt>* )* ( <tt>visitSuperClass</tt> + * <tt>visitInterface</tt>* )</li> + * <li><i>MethodSignature</i> = ( <tt>visitFormalTypeParameter</tt> + * <tt>visitClassBound</tt>? + * <tt>visitInterfaceBound</tt>* )* ( <tt>visitParameterType</tt>* + * <tt>visitReturnType</tt> + * <tt>visitExceptionType</tt>* )</li> <li><i>TypeSignature</i> = + * <tt>visitBaseType</tt> | <tt>visitTypeVariable</tt> | + * <tt>visitArrayType</tt> | ( + * <tt>visitClassType</tt> <tt>visitTypeArgument</tt>* ( + * <tt>visitInnerClassType</tt> <tt>visitTypeArgument</tt>* )* + * <tt>visitEnd</tt> ) )</li> </ul> + * + * @author Thomas Hallgren + * @author Eric Bruneton + */ +public interface SignatureVisitor { + + /** + * Wildcard for an "extends" type argument. + */ + char EXTENDS = '+'; + + /** + * Wildcard for a "super" type argument. + */ + char SUPER = '-'; + + /** + * Wildcard for a normal type argument. + */ + char INSTANCEOF = '='; + + /** + * Visits a formal type parameter. + * + * @param name the name of the formal parameter. + */ + void visitFormalTypeParameter(String name); + + /** + * Visits the class bound of the last visited formal type parameter. + * + * @return a non null visitor to visit the signature of the class bound. + */ + SignatureVisitor visitClassBound(); + + /** + * Visits an interface bound of the last visited formal type parameter. + * + * @return a non null visitor to visit the signature of the interface bound. + */ + SignatureVisitor visitInterfaceBound(); + + /** + * Visits the type of the super class. + * + * @return a non null visitor to visit the signature of the super class + * type. + */ + SignatureVisitor visitSuperclass(); + + /** + * Visits the type of an interface implemented by the class. + * + * @return a non null visitor to visit the signature of the interface type. + */ + SignatureVisitor visitInterface(); + + /** + * Visits the type of a method parameter. + * + * @return a non null visitor to visit the signature of the parameter type. + */ + SignatureVisitor visitParameterType(); + + /** + * Visits the return type of the method. + * + * @return a non null visitor to visit the signature of the return type. + */ + SignatureVisitor visitReturnType(); + + /** + * Visits the type of a method exception. + * + * @return a non null visitor to visit the signature of the exception type. + */ + SignatureVisitor visitExceptionType(); + + /** + * Visits a signature corresponding to a primitive type. + * + * @param descriptor the descriptor of the primitive type, or 'V' for + * <tt>void</tt>. + */ + void visitBaseType(char descriptor); + + /** + * Visits a signature corresponding to a type variable. + * + * @param name the name of the type variable. + */ + void visitTypeVariable(String name); + + /** + * Visits a signature corresponding to an array type. + * + * @return a non null visitor to visit the signature of the array element + * type. + */ + SignatureVisitor visitArrayType(); + + /** + * Starts the visit of a signature corresponding to a class or interface + * type. + * + * @param name the internal name of the class or interface. + */ + void visitClassType(String name); + + /** + * Visits an inner class. + * + * @param name the local name of the inner class in its enclosing class. + */ + void visitInnerClassType(String name); + + /** + * Visits an unbounded type argument of the last visited class or inner + * class type. + */ + void visitTypeArgument(); + + /** + * Visits a type argument of the last visited class or inner class type. + * + * @param wildcard '+', '-' or '='. + * @return a non null visitor to visit the signature of the type argument. + */ + SignatureVisitor visitTypeArgument(char wildcard); + + /** + * Ends the visit of a signature corresponding to a class or interface type. + */ + void visitEnd(); +}
diff --git a/asmx/src/org/objectweb/asm/signature/SignatureWriter.java b/asmx/src/org/objectweb/asm/signature/SignatureWriter.java new file mode 100644 index 0000000..b78d666 --- /dev/null +++ b/asmx/src/org/objectweb/asm/signature/SignatureWriter.java
@@ -0,0 +1,207 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.signature; + +/** + * A signature visitor that generates signatures in string format. + * + * @author Thomas Hallgren + * @author Eric Bruneton + */ +public class SignatureWriter implements SignatureVisitor { + + /** + * Buffer used to construct the signature. + */ + private final StringBuffer buf = new StringBuffer(); + + /** + * Indicates if the signature contains formal type parameters. + */ + private boolean hasFormals; + + /** + * Indicates if the signature contains method parameter types. + */ + private boolean hasParameters; + + /** + * Stack used to keep track of class types that have arguments. Each element + * of this stack is a boolean encoded in one bit. The top of the stack is + * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping = + * /2. + */ + private int argumentStack; + + /** + * Constructs a new {@link SignatureWriter} object. + */ + public SignatureWriter() { + } + + // ------------------------------------------------------------------------ + // Implementation of the SignatureVisitor interface + // ------------------------------------------------------------------------ + + public void visitFormalTypeParameter(String name) { + if (!hasFormals) { + hasFormals = true; + buf.append('<'); + } + buf.append(name); + buf.append(':'); + } + + public SignatureVisitor visitClassBound() { + return this; + } + + public SignatureVisitor visitInterfaceBound() { + buf.append(':'); + return this; + } + + public SignatureVisitor visitSuperclass() { + endFormals(); + return this; + } + + public SignatureVisitor visitInterface() { + return this; + } + + public SignatureVisitor visitParameterType() { + endFormals(); + if (!hasParameters) { + hasParameters = true; + buf.append('('); + } + return this; + } + + public SignatureVisitor visitReturnType() { + endFormals(); + if (!hasParameters) { + buf.append('('); + } + buf.append(')'); + return this; + } + + public SignatureVisitor visitExceptionType() { + buf.append('^'); + return this; + } + + public void visitBaseType(char descriptor) { + buf.append(descriptor); + } + + public void visitTypeVariable(String name) { + buf.append('T'); + buf.append(name); + buf.append(';'); + } + + public SignatureVisitor visitArrayType() { + buf.append('['); + return this; + } + + public void visitClassType(String name) { + buf.append('L'); + buf.append(name); + argumentStack *= 2; + } + + public void visitInnerClassType(String name) { + endArguments(); + buf.append('.'); + buf.append(name); + argumentStack *= 2; + } + + public void visitTypeArgument() { + if (argumentStack % 2 == 0) { + ++argumentStack; + buf.append('<'); + } + buf.append('*'); + } + + public SignatureVisitor visitTypeArgument(char wildcard) { + if (argumentStack % 2 == 0) { + ++argumentStack; + buf.append('<'); + } + if (wildcard != '=') { + buf.append(wildcard); + } + return this; + } + + public void visitEnd() { + endArguments(); + buf.append(';'); + } + + /** + * Returns the signature that was built by this signature writer. + * + * @return the signature that was built by this signature writer. + */ + public String toString() { + return buf.toString(); + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Ends the formal type parameters section of the signature. + */ + private void endFormals() { + if (hasFormals) { + hasFormals = false; + buf.append('>'); + } + } + + /** + * Ends the type arguments of a class or inner class type. + */ + private void endArguments() { + if (argumentStack % 2 == 1) { + buf.append('>'); + } + argumentStack /= 2; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/AbstractInsnNode.java b/asmx/src/org/objectweb/asm/tree/AbstractInsnNode.java new file mode 100644 index 0000000..5a03eab --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/AbstractInsnNode.java
@@ -0,0 +1,206 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import java.util.List; + +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a bytecode instruction. + * + * @author Eric Bruneton + */ +public abstract class AbstractInsnNode { + + /** + * The type of {@link InsnNode} instructions. + */ + public static final int INSN = 0; + + /** + * The type of {@link IntInsnNode} instructions. + */ + public static final int INT_INSN = 1; + + /** + * The type of {@link VarInsnNode} instructions. + */ + public static final int VAR_INSN = 2; + + /** + * The type of {@link TypeInsnNode} instructions. + */ + public static final int TYPE_INSN = 3; + + /** + * The type of {@link FieldInsnNode} instructions. + */ + public static final int FIELD_INSN = 4; + + /** + * The type of {@link MethodInsnNode} instructions. + */ + public static final int METHOD_INSN = 5; + + /** + * The type of {@link InvokeDynamicInsnNode} instructions. + */ + public static final int INVOKE_DYNAMIC_INSN = 6; + + /** + * The type of {@link JumpInsnNode} instructions. + */ + public static final int JUMP_INSN = 7; + + /** + * The type of {@link LabelNode} "instructions". + */ + public static final int LABEL = 8; + + /** + * The type of {@link LdcInsnNode} instructions. + */ + public static final int LDC_INSN = 9; + + /** + * The type of {@link IincInsnNode} instructions. + */ + public static final int IINC_INSN = 10; + + /** + * The type of {@link TableSwitchInsnNode} instructions. + */ + public static final int TABLESWITCH_INSN = 11; + + /** + * The type of {@link LookupSwitchInsnNode} instructions. + */ + public static final int LOOKUPSWITCH_INSN = 12; + + /** + * The type of {@link MultiANewArrayInsnNode} instructions. + */ + public static final int MULTIANEWARRAY_INSN = 13; + + /** + * The type of {@link FrameNode} "instructions". + */ + public static final int FRAME = 14; + + /** + * The type of {@link LineNumberNode} "instructions". + */ + public static final int LINE = 15; + + /** + * The opcode of this instruction. + */ + 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 <tt>null</tt>. + * + * @associates org.objectweb.asm.tree.TypeAnnotationNode + * @label visible + */ + public List<TypeAnnotationNode> 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 <tt>null</tt>. + * + * @associates org.objectweb.asm.tree.TypeAnnotationNode + * @label invisible + */ + public List<TypeAnnotationNode> invisibleTypeAnnotations; + + /** + * Constructs a new {@link AbstractInsnNode}. + * + * @param opcode the opcode of the instruction to be constructed. + */ + protected AbstractInsnNode(final int opcode) { + this.opcode = opcode; + } + + /** + * Returns the opcode of this instruction. + * + * @return the opcode of this instruction. + */ + public int getOpcode() { + return opcode; + } + + /** + * Returns the type of this instruction. + * + * @return the type of this instruction, i.e. one the constants defined in + * this class. + */ + public abstract int getType(); + + /** + * Makes the given code visitor visit this instruction. + * + * @param cv a code visitor. + */ + 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) { + // TODO: needed? + //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)); + //} + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/AnnotationNode.java b/asmx/src/org/objectweb/asm/tree/AnnotationNode.java new file mode 100644 index 0000000..4911e3e --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/AnnotationNode.java
@@ -0,0 +1,187 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.AnnotationVisitor; + +/** + * A node that represents an annotationn. + * + * @author Eric Bruneton + */ +public class AnnotationNode implements AnnotationVisitor { + + /** + * The class descriptor of the annotation class. + */ + public String desc; + + /** + * The name value pairs of this annotation. Each name value pair is stored + * 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 org.objectweb.asm.Type}, or an + * two elements String array (for enumeration values), a + * {@link AnnotationNode}, or a {@link List} of values of one of the + * preceding types. The list may be <tt>null</tt> if there is no name + * value pair. + */ + public List values; + + /** + * Constructs a new {@link AnnotationNode}. + * + * @param desc the class descriptor of the annotation class. + */ + public AnnotationNode(final String desc) { + this.desc = desc; + } + + /** + * Constructs a new {@link AnnotationNode} to visit an array value. + * + * @param values where the visited values must be stored. + */ + AnnotationNode(final List values) { + this.values = values; + } + + // ------------------------------------------------------------------------ + // Implementation of the AnnotationVisitor interface + // ------------------------------------------------------------------------ + + public void visit(final String name, final Object value) { + if (values == null) { + values = new ArrayList(this.desc != null ? 2 : 1); + } + if (this.desc != null) { + values.add(name); + } + values.add(value); + } + + public void visitEnum( + final String name, + final String desc, + final String value) + { + if (values == null) { + values = new ArrayList(this.desc != null ? 2 : 1); + } + if (this.desc != null) { + values.add(name); + } + values.add(new String[] { desc, value }); + } + + public AnnotationVisitor visitAnnotation( + final String name, + final String desc) + { + if (values == null) { + values = new ArrayList(this.desc != null ? 2 : 1); + } + if (this.desc != null) { + values.add(name); + } + AnnotationNode annotation = new AnnotationNode(desc); + values.add(annotation); + return annotation; + } + + public AnnotationVisitor visitArray(final String name) { + if (values == null) { + values = new ArrayList(this.desc != null ? 2 : 1); + } + if (this.desc != null) { + values.add(name); + } + List array = new ArrayList(); + values.add(array); + return new AnnotationNode(array); + } + + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Accept methods + // ------------------------------------------------------------------------ + + /** + * Makes the given visitor visit this annotation. + * + * @param av an annotation visitor. + */ + public void accept(final AnnotationVisitor av) { + if (values != null) { + for (int i = 0; i < values.size(); i += 2) { + String name = (String) values.get(i); + Object value = values.get(i + 1); + accept(av, name, value); + } + } + av.visitEnd(); + } + + /** + * Makes the given visitor visit a given annotation value. + * + * @param av an annotation visitor. + * @param name the value name. + * @param value the actual value. + */ + static void accept( + final AnnotationVisitor av, + final String name, + final Object value) + { + if (value instanceof String[]) { + String[] typeconst = (String[]) value; + av.visitEnum(name, typeconst[0], typeconst[1]); + } else if (value instanceof AnnotationNode) { + AnnotationNode an = (AnnotationNode) value; + 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)); + } + v.visitEnd(); + } else { + av.visit(name, value); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/ClassNode.java b/asmx/src/org/objectweb/asm/tree/ClassNode.java new file mode 100644 index 0000000..d21745b --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/ClassNode.java
@@ -0,0 +1,289 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a class. + * + * @author Eric Bruneton + */ +public class ClassNode extends MemberNode implements ClassVisitor { + + public TypeAnnotationVisitor visitTypeAnnotation( + String desc, boolean visible, boolean inCode) { + throw new RuntimeException("Jaime did not implement yet"); + } + + /** + * The class version. + */ + public int version; + + /** + * The class's access flags (see {@link org.objectweb.asm.Opcodes}). This + * field also indicates if the class is deprecated. + */ + public int access; + + /** + * The internal name of the class (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). + */ + public String name; + + /** + * The signature of the class. Mayt be <tt>null</tt>. + */ + public String signature; + + /** + * The internal of name of the super class (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). For + * interfaces, the super class is {@link Object}. May be <tt>null</tt>, + * but only for the {@link Object} class. + */ + public String superName; + + /** + * The internal names of the class's interfaces (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). This + * list is a list of {@link String} objects. + */ + public List interfaces; + + /** + * The name of the source file from which this class was compiled. May be + * <tt>null</tt>. + */ + public String sourceFile; + + /** + * Debug information to compute the correspondance between source and + * compiled elements of the class. May be <tt>null</tt>. + */ + public String sourceDebug; + + /** + * The internal name of the enclosing class of the class. May be + * <tt>null</tt>. + */ + public String outerClass; + + /** + * The name of the method that contains the class, or <tt>null</tt> if the + * class is not enclosed in a method. + */ + public String outerMethod; + + /** + * The descriptor of the method that contains the class, or <tt>null</tt> + * if the class is not enclosed in a method. + */ + public String outerMethodDesc; + + /** + * Informations about the inner classes of this class. This list is a list + * of {@link InnerClassNode} objects. + * + * @associates org.objectweb.asm.tree.InnerClassNode + */ + public List innerClasses; + + /** + * The fields of this class. This list is a list of {@link FieldNode} + * objects. + * + * @associates org.objectweb.asm.tree.FieldNode + */ + public List fields; + + /** + * The methods of this class. This list is a list of {@link MethodNode} + * objects. + * + * @associates org.objectweb.asm.tree.MethodNode + */ + public List methods; + + /** + * Constructs a new {@link ClassNode}. + */ + public ClassNode() { + this.interfaces = new ArrayList(); + this.innerClasses = new ArrayList(); + this.fields = new ArrayList(); + this.methods = new ArrayList(); + } + + // ------------------------------------------------------------------------ + // Implementation of the ClassVisitor interface + // ------------------------------------------------------------------------ + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + this.version = version; + this.access = access; + this.name = name; + this.signature = signature; + this.superName = superName; + if (interfaces != null) { + this.interfaces.addAll(Arrays.asList(interfaces)); + } + } + + public void visitSource(final String file, final String debug) { + sourceFile = file; + sourceDebug = debug; + } + + public void visitOuterClass( + final String owner, + final String name, + final String desc) + { + outerClass = owner; + outerMethod = name; + outerMethodDesc = desc; + } + + public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access) + { + InnerClassNode icn = new InnerClassNode(name, + outerName, + innerName, + access); + innerClasses.add(icn); + } + + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + FieldNode fn = new FieldNode(access, name, desc, signature, value); + fields.add(fn); + return fn; + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + MethodNode mn = new MethodNode(access, + name, + desc, + signature, + exceptions); + methods.add(mn); + return mn; + } + + public void visitEnd() { + } + + // ------------------------------------------------------------------------ + // Accept method + // ------------------------------------------------------------------------ + + /** + * Makes the given class visitor visit this class. + * + * @param cv a class visitor. + */ + public void accept(final ClassVisitor cv) { + // visits header + String[] interfaces = new String[this.interfaces.size()]; + this.interfaces.toArray(interfaces); + cv.visit(version, access, name, signature, superName, interfaces); + // visits source + if (sourceFile != null || sourceDebug != null) { + cv.visitSource(sourceFile, sourceDebug); + } + // visits outer class + if (outerClass != null) { + cv.visitOuterClass(outerClass, outerMethod, outerMethodDesc); + } + // visits attributes + int i, n; + n = visibleAnnotations == null ? 0 : visibleAnnotations.size(); + for (i = 0; i < n; ++i) { + AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i); + an.accept(cv.visitAnnotation(an.desc, true)); + } + n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size(); + for (i = 0; i < n; ++i) { + AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i); + an.accept(cv.visitAnnotation(an.desc, false)); + } + n = attrs == null ? 0 : attrs.size(); + for (i = 0; i < n; ++i) { + cv.visitAttribute((Attribute) attrs.get(i)); + } + // visits inner classes + for (i = 0; i < innerClasses.size(); ++i) { + ((InnerClassNode) innerClasses.get(i)).accept(cv); + } + // visits fields + for (i = 0; i < fields.size(); ++i) { + ((FieldNode) fields.get(i)).accept(cv); + } + // visits methods + for (i = 0; i < methods.size(); ++i) { + ((MethodNode) methods.get(i)).accept(cv); + } + // visits end + cv.visitEnd(); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/FieldInsnNode.java b/asmx/src/org/objectweb/asm/tree/FieldInsnNode.java new file mode 100644 index 0000000..4399e3a --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/FieldInsnNode.java
@@ -0,0 +1,97 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a field instruction. A field instruction is an + * instruction that loads or stores the value of a field of an object. + * + * @author Eric Bruneton + */ +public class FieldInsnNode extends AbstractInsnNode { + + /** + * The internal name of the field's owner class (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). + */ + public String owner; + + /** + * The field's name. + */ + public String name; + + /** + * The field's descriptor (see {@link org.objectweb.asm.Type}). + */ + public String desc; + + /** + * Constructs a new {@link FieldInsnNode}. + * + * @param opcode the opcode of the type instruction to be constructed. This + * opcode must be GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner the internal name of the field's owner class (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link org.objectweb.asm.Type}). + */ + public FieldInsnNode( + final int opcode, + final String owner, + final String name, + final String desc) + { + super(opcode); + this.owner = owner; + this.name = name; + this.desc = desc; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be GETSTATIC, + * PUTSTATIC, GETFIELD or PUTFIELD. + */ + public void setOpcode(final int opcode) { + this.opcode = opcode; + } + + public void accept(final MethodVisitor cv) { + cv.visitFieldInsn(opcode, owner, name, desc); + } + + public int getType() { + return FIELD_INSN; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/FieldNode.java b/asmx/src/org/objectweb/asm/tree/FieldNode.java new file mode 100644 index 0000000..af69382 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/FieldNode.java
@@ -0,0 +1,133 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; + +/** + * A node that represents a field. + * + * @author Eric Bruneton + */ +public class FieldNode extends MemberNode implements FieldVisitor { + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + throw new RuntimeException("Jaime did not implement yet"); + } + //end jaime + + /** + * The field's access flags (see {@link org.objectweb.asm.Opcodes}). This + * field also indicates if the field is synthetic and/or deprecated. + */ + public int access; + + /** + * The field's name. + */ + public String name; + + /** + * The field's descriptor (see {@link org.objectweb.asm.Type}). + */ + public String desc; + + /** + * The field's signature. May be <tt>null</tt>. + */ + public String signature; + + /** + * The field's initial value. This field, which may be <tt>null</tt> 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}. + */ + public Object value; + + /** + * Constructs a new {@link FieldNode}. + * + * @param access the field's access flags (see + * {@link org.objectweb.asm.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 org.objectweb.asm.Type}). + * @param signature the field's signature. + * @param value the field's initial value. This parameter, which may be + * <tt>null</tt> 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}. + */ + public FieldNode( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + this.access = access; + this.name = name; + this.desc = desc; + this.signature = signature; + this.value = value; + } + + /** + * Makes the given class visitor visit this field. + * + * @param cv a class visitor. + */ + public void accept(final ClassVisitor cv) { + FieldVisitor fv = cv.visitField(access, name, desc, signature, value); + int i, n; + n = visibleAnnotations == null ? 0 : visibleAnnotations.size(); + for (i = 0; i < n; ++i) { + AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i); + an.accept(fv.visitAnnotation(an.desc, true)); + } + n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size(); + for (i = 0; i < n; ++i) { + AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i); + an.accept(fv.visitAnnotation(an.desc, false)); + } + n = attrs == null ? 0 : attrs.size(); + for (i = 0; i < n; ++i) { + fv.visitAttribute((Attribute) attrs.get(i)); + } + fv.visitEnd(); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/IincInsnNode.java b/asmx/src/org/objectweb/asm/tree/IincInsnNode.java new file mode 100644 index 0000000..e7e79dc --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/IincInsnNode.java
@@ -0,0 +1,71 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A node that represents an IINC instruction. + * + * @author Eric Bruneton + */ +public class IincInsnNode extends AbstractInsnNode { + + /** + * Index of the local variable to be incremented. + */ + public int var; + + /** + * Amount to increment the local variable by. + */ + public int incr; + + /** + * Constructs a new {@link IincInsnNode}. + * + * @param var index of the local variable to be incremented. + * @param incr increment amount to increment the local variable by. + */ + public IincInsnNode(final int var, final int incr) { + super(Opcodes.IINC); + this.var = var; + this.incr = incr; + } + + public void accept(final MethodVisitor mv) { + mv.visitIincInsn(var, incr); + } + + public int getType() { + return IINC_INSN; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/InnerClassNode.java b/asmx/src/org/objectweb/asm/tree/InnerClassNode.java new file mode 100644 index 0000000..a325317 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/InnerClassNode.java
@@ -0,0 +1,101 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.ClassVisitor; + +/** + * A node that represents an inner class. + * + * @author Eric Bruneton + */ +public class InnerClassNode { + + /** + * The internal name of an inner class (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). + */ + public String name; + + /** + * The internal name of the class to which the inner class belongs (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). May + * be <tt>null</tt>. + */ + public String outerName; + + /** + * The (simple) name of the inner class inside its enclosing class. May be + * <tt>null</tt> for anonymous inner classes. + */ + public String innerName; + + /** + * The access flags of the inner class as originally declared in the + * enclosing class. + */ + public int access; + + /** + * Constructs a new {@link InnerClassNode}. + * + * @param name the internal name of an inner class (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). + * @param outerName the internal name of the class to which the inner class + * belongs (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). + * May be <tt>null</tt>. + * @param innerName the (simple) name of the inner class inside its + * enclosing class. May be <tt>null</tt> for anonymous inner + * classes. + * @param access the access flags of the inner class as originally declared + * in the enclosing class. + */ + public InnerClassNode( + final String name, + final String outerName, + final String innerName, + final int access) + { + this.name = name; + this.outerName = outerName; + this.innerName = innerName; + this.access = access; + } + + /** + * Makes the given class visitor visit this inner class. + * + * @param cv a class visitor. + */ + public void accept(final ClassVisitor cv) { + cv.visitInnerClass(name, outerName, innerName, access); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/InsnNode.java b/asmx/src/org/objectweb/asm/tree/InsnNode.java new file mode 100644 index 0000000..434dd5b --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/InsnNode.java
@@ -0,0 +1,96 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a zero operand instruction. + * + * @author Eric Bruneton + */ +public class InsnNode extends AbstractInsnNode { + + private final static InsnNode[] INSNS; + + static { + INSNS = new InsnNode[255]; + for (int i = 0; i < INSNS.length; ++i) { + INSNS[i] = new InsnNode(i); + } + } + + /** + * Returns the {@link InsnNode} corresponding to the given opcode. + * + * @deprecated uses the constructor instead. + * + * @param opcode an instruction opcode. + * @return the {@link InsnNode} corresponding to the given opcode. + */ + public final static InsnNode getByOpcode(final int opcode) { + return INSNS[opcode]; + } + + /** + * Constructs a new {@link InsnNode}. + * + * @param opcode the opcode of the instruction to be constructed. This + * opcode must be 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 InsnNode(final int opcode) { + super(opcode); + } + + /** + * Makes the given visitor visit this instruction. + * + * @param mv a method visitor. + */ + public void accept(final MethodVisitor mv) { + mv.visitInsn(opcode); + } + + public int getType() { + return INSN; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/IntInsnNode.java b/asmx/src/org/objectweb/asm/tree/IntInsnNode.java new file mode 100644 index 0000000..2c200a5 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/IntInsnNode.java
@@ -0,0 +1,75 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents an instruction with a single int operand. + * + * @author Eric Bruneton + */ +public class IntInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. + */ + public int operand; + + /** + * Constructs a new {@link IntInsnNode}. + * + * @param opcode the opcode of the instruction to be constructed. This + * opcode must be BIPUSH, SIPUSH or NEWARRAY. + * @param operand the operand of the instruction to be constructed. + */ + public IntInsnNode(final int opcode, final int operand) { + super(opcode); + this.operand = operand; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be BIPUSH, + * SIPUSH or NEWARRAY. + */ + public void setOpcode(final int opcode) { + this.opcode = opcode; + } + + public void accept(final MethodVisitor mv) { + mv.visitIntInsn(opcode, operand); + } + + public int getType() { + return INT_INSN; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/InvokeDynamicInsnNode.java b/asmx/src/org/objectweb/asm/tree/InvokeDynamicInsnNode.java new file mode 100644 index 0000000..c1f7a3f --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
@@ -0,0 +1,63 @@ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A node that represents an INVOKEDYNAMIC instruction. + */ +public class InvokeDynamicInsnNode extends AbstractInsnNode { + + /** + * Invokedynamic name. + */ + public String name; + + /** + * Invokedynamic descriptor. + */ + public String desc; + + /** + * Bootstrap method + */ + public Handle bsm; + + /** + * Bootstrap constant arguments + */ + public Object[] bsmArgs; + + /** + * Constructs a new {@link InvokeDynamicInsnNode}. + * + * @param name + * invokedynamic name. + * @param desc + * invokedynamic descriptor (see {@link org.objectweb.asm.Type}). + * @param bsm + * the bootstrap method. + * @param bsmArgs + * the boostrap constant arguments. + */ + public InvokeDynamicInsnNode(final String name, final String desc, + final Handle bsm, final Object... bsmArgs) { + super(Opcodes.INVOKEDYNAMIC); + this.name = name; + this.desc = desc; + this.bsm = bsm; + this.bsmArgs = bsmArgs; + } + + @Override + public int getType() { + return INVOKE_DYNAMIC_INSN; + } + + @Override + public void accept(final MethodVisitor mv) { + mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); + acceptAnnotations(mv); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/JumpInsnNode.java b/asmx/src/org/objectweb/asm/tree/JumpInsnNode.java new file mode 100644 index 0000000..5ab6e11 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/JumpInsnNode.java
@@ -0,0 +1,84 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a jump instruction. A jump instruction is an + * instruction that may jump to another instruction. + * + * @author Eric Bruneton + */ +public class JumpInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. This operand is a label that designates + * the instruction to which this instruction may jump. + */ + public Label label; + + /** + * Constructs a new {@link JumpInsnNode}. + * + * @param opcode the opcode of the type instruction to be constructed. This + * opcode must be 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 constructed. This + * operand is a label that designates the instruction to which the + * jump instruction may jump. + */ + public JumpInsnNode(final int opcode, final Label label) { + super(opcode); + this.label = label; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be 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. + */ + public void setOpcode(final int opcode) { + this.opcode = opcode; + } + + public void accept(final MethodVisitor mv) { + mv.visitJumpInsn(opcode, label); + } + + public int getType() { + return JUMP_INSN; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/LabelNode.java b/asmx/src/org/objectweb/asm/tree/LabelNode.java new file mode 100644 index 0000000..5eead6e --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/LabelNode.java
@@ -0,0 +1,54 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; + +/** + * An {@link AbstractInsnNode} that encapsulates a {@link Label}. + */ +public class LabelNode extends AbstractInsnNode { + + public Label label; + + public LabelNode(final Label label) { + super(-1); + this.label = label; + } + + public void accept(final MethodVisitor cv) { + cv.visitLabel(label); + } + + public int getType() { + return LABEL; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/LdcInsnNode.java b/asmx/src/org/objectweb/asm/tree/LdcInsnNode.java new file mode 100644 index 0000000..ef95513 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/LdcInsnNode.java
@@ -0,0 +1,68 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A node that represents an LDC instruction. + * + * @author Eric Bruneton + */ +public class LdcInsnNode extends AbstractInsnNode { + + /** + * 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} or a {@link org.objectweb.asm.Type}. + */ + public Object cst; + + /** + * Constructs a new {@link LdcInsnNode}. + * + * @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} or a {@link String}. + */ + public LdcInsnNode(final Object cst) { + super(Opcodes.LDC); + this.cst = cst; + } + + public void accept(final MethodVisitor mv) { + mv.visitLdcInsn(cst); + } + + public int getType() { + return LDC_INSN; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/LineNumberNode.java b/asmx/src/org/objectweb/asm/tree/LineNumberNode.java new file mode 100644 index 0000000..0d49747 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/LineNumberNode.java
@@ -0,0 +1,73 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a line number declaration. + * + * @author Eric Bruneton + */ +public class LineNumberNode { + + /** + * A line number. This number refers to the source file from which the class + * was compiled. + */ + public int line; + + /** + * The first instruction corresponding to this line number. + */ + public Label start; + + /** + * Constructs a new {@link LineNumberNode}. + * + * @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. + */ + public LineNumberNode(final int line, final Label start) { + this.line = line; + this.start = start; + } + + /** + * Makes the given visitor visit this line number declaration. + * + * @param mv a method visitor. + */ + public void accept(final MethodVisitor mv) { + mv.visitLineNumber(line, start); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/LocalVariableNode.java b/asmx/src/org/objectweb/asm/tree/LocalVariableNode.java new file mode 100644 index 0000000..b9efe44 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/LocalVariableNode.java
@@ -0,0 +1,111 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Label; + +/** + * A node that represents a local variable declaration. + * + * @author Eric Bruneton + */ +public class LocalVariableNode { + + /** + * The name of a local variable. + */ + public String name; + + /** + * The type descriptor of this local variable. + */ + public String desc; + + /** + * The signature of this local variable. May be <tt>null</tt>. + */ + public String signature; + + /** + * The first instruction corresponding to the scope of this local variable + * (inclusive). + */ + public Label start; + + /** + * The last instruction corresponding to the scope of this local variable + * (exclusive). + */ + public Label end; + + /** + * The local variable's index. + */ + public int index; + + /** + * Constructs a new {@link LocalVariableNode}. + * + * @param name the name of a local variable. + * @param desc the type descriptor of this local variable. + * @param signature the signature of this local variable. May be + * <tt>null</tt>. + * @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. + */ + public LocalVariableNode( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + this.name = name; + this.desc = desc; + this.signature = signature; + this.start = start; + this.end = end; + this.index = index; + } + + /** + * Makes the given visitor visit this local variable declaration. + * + * @param mv a method visitor. + */ + public void accept(final MethodVisitor mv) { + mv.visitLocalVariable(name, desc, signature, start, end, index); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/LookupSwitchInsnNode.java b/asmx/src/org/objectweb/asm/tree/LookupSwitchInsnNode.java new file mode 100644 index 0000000..1c8e69f --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/LookupSwitchInsnNode.java
@@ -0,0 +1,103 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.MethodVisitor; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * A node that represents a LOOKUPSWITCH instruction. + * + * @author Eric Bruneton + */ +public class LookupSwitchInsnNode extends AbstractInsnNode { + + /** + * Beginning of the default handler block. + */ + public Label dflt; + + /** + * The values of the keys. This list is a list of {@link Integer} objects. + */ + public List keys; + + /** + * Beginnings of the handler blocks. This list is a list of {@link Label} + * objects. + */ + public List labels; + + /** + * Constructs a new {@link LookupSwitchInsnNode}. + * + * @param dflt beginning of the default handler block. + * @param keys the values of the keys. + * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is + * the beginning of the handler block for the <tt>keys[i]</tt> key. + */ + public LookupSwitchInsnNode( + final Label dflt, + final int[] keys, + final Label[] labels) + { + super(Opcodes.LOOKUPSWITCH); + this.dflt = dflt; + this.keys = new ArrayList(keys == null ? 0 : keys.length); + this.labels = new ArrayList(labels == null ? 0 : labels.length); + if (keys != null) { + for (int i = 0; i < keys.length; ++i) { + this.keys.add(new Integer(keys[i])); + } + } + if (labels != null) { + this.labels.addAll(Arrays.asList(labels)); + } + } + + public void accept(final MethodVisitor mv) { + int[] keys = new int[this.keys.size()]; + for (int i = 0; i < keys.length; ++i) { + keys[i] = ((Integer) this.keys.get(i)).intValue(); + } + Label[] labels = new Label[this.labels.size()]; + this.labels.toArray(labels); + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + + public int getType() { + return LOOKUPSWITCH_INSN; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/MemberNode.java b/asmx/src/org/objectweb/asm/tree/MemberNode.java new file mode 100644 index 0000000..86b1c5c --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/MemberNode.java
@@ -0,0 +1,120 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; + +/** + * An abstract class, field or method node. + * + * @author Eric Bruneton + */ +public abstract class MemberNode { + + /** + * The runtime visible annotations of this class, field or method. This list + * is a list of {@link AnnotationNode} objects. May be <tt>null</tt>. + * + * @associates org.objectweb.asm.tree.AnnotationNode + * @label visible + */ + public List visibleAnnotations; + + /** + * The runtime invisible annotations of this class, field or method. This + * list is a list of {@link AnnotationNode} objects. May be <tt>null</tt>. + * + * @associates org.objectweb.asm.tree.AnnotationNode + * @label invisible + */ + public List invisibleAnnotations; + + /** + * The non standard attributes of this class, field or method. This list is + * a list of {@link Attribute} objects. May be <tt>null</tt>. + * + * @associates org.objectweb.asm.Attribute + */ + public List attrs; + + /** + * Constructs a new {@link MemberNode}. + */ + public MemberNode() { + } + + /** + * Visits an annotation of this class, field or method. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values. + */ + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + AnnotationNode an = new AnnotationNode(desc); + if (visible) { + if (visibleAnnotations == null) { + visibleAnnotations = new ArrayList(1); + } + visibleAnnotations.add(an); + } else { + if (invisibleAnnotations == null) { + invisibleAnnotations = new ArrayList(1); + } + invisibleAnnotations.add(an); + } + return an; + } + + /** + * Visits a non standard attribute of this class, field or method. + * + * @param attr an attribute. + */ + public void visitAttribute(final Attribute attr) { + if (attrs == null) { + attrs = new ArrayList(1); + } + attrs.add(attr); + } + + /** + * Visits the end of this class, field or method. + */ + public void visitEnd() { + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/MethodInsnNode.java b/asmx/src/org/objectweb/asm/tree/MethodInsnNode.java new file mode 100644 index 0000000..714aabe --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/MethodInsnNode.java
@@ -0,0 +1,98 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a method instruction. A method instruction is an + * instruction that invokes a method. + * + * @author Eric Bruneton + */ +public class MethodInsnNode extends AbstractInsnNode { + + /** + * The internal name of the method's owner class (see + * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). + */ + public String owner; + + /** + * The method's name. + */ + public String name; + + /** + * The method's descriptor (see {@link org.objectweb.asm.Type}). + */ + public String desc; + + /** + * 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 org.objectweb.asm.Type#getInternalName() getInternalName}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link org.objectweb.asm.Type}). + */ + public MethodInsnNode( + final int opcode, + final String owner, + final String name, + final String desc) + { + super(opcode); + this.owner = owner; + this.name = name; + this.desc = desc; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be + * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE. + */ + public void setOpcode(final int opcode) { + this.opcode = opcode; + } + + public void accept(final MethodVisitor mv) { + mv.visitMethodInsn(opcode, owner, name, desc); + } + + public int getType() { + return METHOD_INSN; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/MethodNode.java b/asmx/src/org/objectweb/asm/tree/MethodNode.java new file mode 100644 index 0000000..4711717 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/MethodNode.java
@@ -0,0 +1,468 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.TypePath; + +/** + * A node that represents a method. + * + * @author Eric Bruneton + */ +public class MethodNode extends MemberNode implements MethodVisitor { + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + return null; + } + //end jaime + + /** + * The method's access flags (see {@link Opcodes}). This field also + * indicates if the method is synthetic and/or deprecated. + */ + public int access; + + /** + * The method's name. + */ + public String name; + + /** + * The method's descriptor (see {@link Type}). + */ + public String desc; + + /** + * The method's signature. May be <tt>null</tt>. + */ + public String signature; + + /** + * The internal names of the method's exception classes (see + * {@link Type#getInternalName() getInternalName}). This list is a list of + * {@link String} objects. + */ + public List exceptions; + + /** + * The default value of this annotation interface method. This field must be + * a {@link Byte}, {@link Boolean}, {@link Character}, {@link Short}, + * {@link Integer}, {@link Long}, {@link Float}, {@link Double}, + * {@link String} or {@link Type}, or an two elements String array (for + * enumeration values), a {@link AnnotationNode}, or a {@link List} of + * values of one of the preceding types. May be <tt>null</tt>. + */ + public Object annotationDefault; + + /** + * The runtime visible parameter annotations of this method. These lists are + * lists of {@link AnnotationNode} objects. May be <tt>null</tt>. + * + * @associates org.objectweb.asm.tree.AnnotationNode + * @label invisible parameters + */ + public List[] visibleParameterAnnotations; + + /** + * The runtime invisible parameter annotations of this method. These lists + * are lists of {@link AnnotationNode} objects. May be <tt>null</tt>. + * + * @associates org.objectweb.asm.tree.AnnotationNode + * @label visible parameters + */ + public List[] invisibleParameterAnnotations; + + /** + * The instructions of this method. This list is a list of + * {@link AbstractInsnNode} objects. + * + * @associates org.objectweb.asm.tree.AbstractInsnNode + * @label instructions + */ + public List instructions; + + /** + * The try catch blocks of this method. This list is a list of + * {@link TryCatchBlockNode} objects. + * + * @associates org.objectweb.asm.tree.TryCatchBlockNode + */ + public List tryCatchBlocks; + + /** + * The maximum stack size of this method. + */ + public int maxStack; + + /** + * The maximum number of local variables of this method. + */ + public int maxLocals; + + /** + * The local variables of this method. This list is a list of + * {@link LocalVariableNode} objects. May be <tt>null</tt> + * + * @associates org.objectweb.asm.tree.LocalVariableNode + */ + public List localVariables; + + /** + * The line numbers of this method. This list is a list of + * {@link LineNumberNode} objects. May be <tt>null</tt> + * + * @associates org.objectweb.asm.tree.LineNumberNode + */ + public List lineNumbers; + + /** + * Constructs a new {@link MethodNode}. + * + * @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 Type}). + * @param signature the method's signature. May be <tt>null</tt>. + * @param exceptions the internal names of the method's exception classes + * (see {@link Type#getInternalName() getInternalName}). May be + * <tt>null</tt>. + */ + public MethodNode( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + this.access = access; + this.name = name; + this.desc = desc; + this.signature = signature; + this.exceptions = new ArrayList(exceptions == null + ? 0 + : exceptions.length); + boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0; + this.instructions = new ArrayList(isAbstract ? 0 : 24); + if (!isAbstract) { + this.localVariables = new ArrayList(5); + this.lineNumbers = new ArrayList(5); + } + this.tryCatchBlocks = new ArrayList(); + if (exceptions != null) { + this.exceptions.addAll(Arrays.asList(exceptions)); + } + } + + // ------------------------------------------------------------------------ + // Implementation of the MethodVisitor interface + // ------------------------------------------------------------------------ + + public AnnotationVisitor visitAnnotationDefault() { + return new AnnotationNode(new ArrayList(0) { + public boolean add(Object o) { + annotationDefault = o; + return super.add(o); + } + }); + } + + public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible) + { + AnnotationNode an = new AnnotationNode(desc); + if (visible) { + if (visibleParameterAnnotations == null) { + int params = Type.getArgumentTypes(this.desc).length; + visibleParameterAnnotations = new List[params]; + } + if (visibleParameterAnnotations[parameter] == null) { + visibleParameterAnnotations[parameter] = new ArrayList(1); + } + visibleParameterAnnotations[parameter].add(an); + } else { + if (invisibleParameterAnnotations == null) { + int params = Type.getArgumentTypes(this.desc).length; + invisibleParameterAnnotations = new List[params]; + } + if (invisibleParameterAnnotations[parameter] == null) { + invisibleParameterAnnotations[parameter] = new ArrayList(1); + } + invisibleParameterAnnotations[parameter].add(an); + } + return an; + } + + public void visitCode() { + } + + public void visitInsn(final int opcode) { + instructions.add(new InsnNode(opcode)); + } + + public void visitIntInsn(final int opcode, final int operand) { + instructions.add(new IntInsnNode(opcode, operand)); + } + + public void visitVarInsn(final int opcode, final int var) { + instructions.add(new VarInsnNode(opcode, var)); + } + + public void visitTypeInsn(final int opcode, final String desc) { + instructions.add(new TypeInsnNode(opcode, desc)); + } + + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + instructions.add(new FieldInsnNode(opcode, owner, name, desc)); + } + + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + instructions.add(new MethodInsnNode(opcode, owner, name, desc)); + } + + @Override + public void visitInvokeDynamicInsn( + String name, + String desc, + Handle bsm, + Object... bsmArgs) + { + instructions.add(new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs)); + } + + public void visitJumpInsn(final int opcode, final Label label) { + instructions.add(new JumpInsnNode(opcode, label)); + } + + public void visitLabel(final Label label) { + instructions.add(new LabelNode(label)); + } + + public void visitLdcInsn(final Object cst) { + instructions.add(new LdcInsnNode(cst)); + } + + public void visitIincInsn(final int var, final int increment) { + instructions.add(new IincInsnNode(var, increment)); + } + + public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label[] labels) + { + instructions.add(new TableSwitchInsnNode(min, max, dflt, labels)); + } + + public void visitLookupSwitchInsn( + final Label dflt, + final int[] keys, + final Label[] labels) + { + instructions.add(new LookupSwitchInsnNode(dflt, keys, labels)); + } + + public void visitMultiANewArrayInsn(final String desc, final int dims) { + instructions.add(new MultiANewArrayInsnNode(desc, dims)); + } + + public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type) + { + tryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, type)); + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + localVariables.add(new LocalVariableNode(name, + desc, + signature, + start, + end, + index)); + } + + public void visitLineNumber(final int line, final Label start) { + lineNumbers.add(new LineNumberNode(line, start)); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } + + // ------------------------------------------------------------------------ + // Accept method + // ------------------------------------------------------------------------ + + /** + * Makes the given class visitor visit this method. + * + * @param cv a class visitor. + */ + public void accept(final ClassVisitor cv) { + String[] exceptions = new String[this.exceptions.size()]; + this.exceptions.toArray(exceptions); + MethodVisitor mv = cv.visitMethod(access, + name, + desc, + signature, + exceptions); + if (mv != null) { + accept(mv); + } + } + + /** + * Makes the given method visitor visit this method. + * + * @param mv a method visitor. + */ + public void accept(final MethodVisitor mv) { + // visits the method attributes + int i, j, n; + if (annotationDefault != null) { + AnnotationVisitor av = mv.visitAnnotationDefault(); + AnnotationNode.accept(av, null, annotationDefault); + av.visitEnd(); + } + n = visibleAnnotations == null ? 0 : visibleAnnotations.size(); + for (i = 0; i < n; ++i) { + AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i); + an.accept(mv.visitAnnotation(an.desc, true)); + } + n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size(); + for (i = 0; i < n; ++i) { + AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i); + an.accept(mv.visitAnnotation(an.desc, false)); + } + n = visibleParameterAnnotations == null + ? 0 + : visibleParameterAnnotations.length; + for (i = 0; i < n; ++i) { + List l = visibleParameterAnnotations[i]; + if (l == null) { + continue; + } + for (j = 0; j < l.size(); ++j) { + AnnotationNode an = (AnnotationNode) l.get(j); + an.accept(mv.visitParameterAnnotation(i, an.desc, true)); + } + } + n = invisibleParameterAnnotations == null + ? 0 + : invisibleParameterAnnotations.length; + for (i = 0; i < n; ++i) { + List l = invisibleParameterAnnotations[i]; + if (l == null) { + continue; + } + for (j = 0; j < l.size(); ++j) { + AnnotationNode an = (AnnotationNode) l.get(j); + an.accept(mv.visitParameterAnnotation(i, an.desc, false)); + } + } + n = attrs == null ? 0 : attrs.size(); + for (i = 0; i < n; ++i) { + mv.visitAttribute((Attribute) attrs.get(i)); + } + // visits the method's code + if (instructions.size() > 0) { + mv.visitCode(); + // visits try catch blocks + for (i = 0; i < tryCatchBlocks.size(); ++i) { + ((TryCatchBlockNode) tryCatchBlocks.get(i)).accept(mv); + } + // visits instructions + for (i = 0; i < instructions.size(); ++i) { + ((AbstractInsnNode) instructions.get(i)).accept(mv); + } + // visits local variables + n = localVariables == null ? 0 : localVariables.size(); + for (i = 0; i < n; ++i) { + ((LocalVariableNode) localVariables.get(i)).accept(mv); + } + // visits line numbers + n = lineNumbers == null ? 0 : lineNumbers.size(); + for (i = 0; i < n; ++i) { + ((LineNumberNode) lineNumbers.get(i)).accept(mv); + } + // visits maxs + mv.visitMaxs(maxStack, maxLocals); + } + mv.visitEnd(); + } + + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + // TODO Auto-generated method stub + return null; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/MultiANewArrayInsnNode.java b/asmx/src/org/objectweb/asm/tree/MultiANewArrayInsnNode.java new file mode 100644 index 0000000..a9cb3c1 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
@@ -0,0 +1,71 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a MULTIANEWARRAY instruction. + * + * @author Eric Bruneton + */ +public class MultiANewArrayInsnNode extends AbstractInsnNode { + + /** + * An array type descriptor (see {@link org.objectweb.asm.Type}). + */ + public String desc; + + /** + * Number of dimensions of the array to allocate. + */ + public int dims; + + /** + * Constructs a new {@link MultiANewArrayInsnNode}. + * + * @param desc an array type descriptor (see {@link org.objectweb.asm.Type}). + * @param dims number of dimensions of the array to allocate. + */ + public MultiANewArrayInsnNode(final String desc, final int dims) { + super(Opcodes.MULTIANEWARRAY); + this.desc = desc; + this.dims = dims; + } + + public void accept(final MethodVisitor mv) { + mv.visitMultiANewArrayInsn(desc, dims); + } + + public int getType() { + return MULTIANEWARRAY_INSN; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/TableSwitchInsnNode.java b/asmx/src/org/objectweb/asm/tree/TableSwitchInsnNode.java new file mode 100644 index 0000000..fa5e3f8 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/TableSwitchInsnNode.java
@@ -0,0 +1,102 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.MethodVisitor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A node that represents a TABLESWITCH instruction. + * + * @author Eric Bruneton + */ +public class TableSwitchInsnNode extends AbstractInsnNode { + + /** + * The minimum key value. + */ + public int min; + + /** + * The maximum key value. + */ + public int max; + + /** + * Beginning of the default handler block. + */ + public Label dflt; + + /** + * Beginnings of the handler blocks. This list is a list of {@link Label} + * objects. + */ + public List labels; + + /** + * Constructs a new {@link TableSwitchInsnNode}. + * + * @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. <tt>labels[i]</tt> is + * the beginning of the handler block for the <tt>min + i</tt> key. + */ + public TableSwitchInsnNode( + final int min, + final int max, + final Label dflt, + final Label[] labels) + { + super(Opcodes.TABLESWITCH); + this.min = min; + this.max = max; + this.dflt = dflt; + this.labels = new ArrayList(); + if (labels != null) { + this.labels.addAll(Arrays.asList(labels)); + } + } + + public void accept(final MethodVisitor mv) { + Label[] labels = new Label[this.labels.size()]; + this.labels.toArray(labels); + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + + public int getType() { + return TABLESWITCH_INSN; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/TryCatchBlockNode.java b/asmx/src/org/objectweb/asm/tree/TryCatchBlockNode.java new file mode 100644 index 0000000..18cd310 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/TryCatchBlockNode.java
@@ -0,0 +1,93 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a try catch block. + * + * @author Eric Bruneton + */ +public class TryCatchBlockNode { + + /** + * Beginning of the exception handler's scope (inclusive). + */ + public Label start; + + /** + * End of the exception handler's scope (exclusive). + */ + public Label end; + + /** + * Beginning of the exception handler's code. + */ + public Label handler; + + /** + * Internal name of the type of exceptions handled by the handler. May be + * <tt>null</tt> to catch any exceptions (for "finally" blocks). + */ + public String type; + + /** + * Constructs a new {@link TryCatchBlockNode}. + * + * @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 <tt>null</tt> to catch any exceptions (for "finally" + * blocks). + */ + public TryCatchBlockNode( + final Label start, + final Label end, + final Label handler, + final String type) + { + this.start = start; + this.end = end; + this.handler = handler; + this.type = type; + } + + /** + * Makes the given visitor visit this try catch block. + * + * @param mv a method visitor. + */ + public void accept(final MethodVisitor mv) { + mv.visitTryCatchBlock(start, end, handler, type); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/TypeAnnotationNode.java b/asmx/src/org/objectweb/asm/tree/TypeAnnotationNode.java new file mode 100644 index 0000000..b582146 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/TypeAnnotationNode.java
@@ -0,0 +1,10 @@ +package org.objectweb.asm.tree; + +import org.objectweb.asm.TypePath; + +public class TypeAnnotationNode extends AnnotationNode { + public TypeAnnotationNode(final int typeRef, + final TypePath typePath, final String desc) { + super(desc); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/TypeInsnNode.java b/asmx/src/org/objectweb/asm/tree/TypeInsnNode.java new file mode 100644 index 0000000..67c6b84 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/TypeInsnNode.java
@@ -0,0 +1,78 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a type instruction. A type instruction is an + * instruction that takes a type descriptor as parameter. + * + * @author Eric Bruneton + */ +public class TypeInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. This operand is a type descriptor (see + * {@link org.objectweb.asm.Type}). + */ + public String desc; + + /** + * Constructs a new {@link TypeInsnNode}. + * + * @param opcode the opcode of the type instruction to be constructed. This + * opcode must be NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param desc the operand of the instruction to be constructed. This + * operand is a type descriptor (see {@link org.objectweb.asm.Type}). + */ + public TypeInsnNode(final int opcode, final String desc) { + super(opcode); + this.desc = desc; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be NEW, + * ANEWARRAY, CHECKCAST or INSTANCEOF. + */ + public void setOpcode(final int opcode) { + this.opcode = opcode; + } + + public void accept(final MethodVisitor mv) { + mv.visitTypeInsn(opcode, desc); + } + + public int getType() { + return TYPE_INSN; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/VarInsnNode.java b/asmx/src/org/objectweb/asm/tree/VarInsnNode.java new file mode 100644 index 0000000..2fe3a95 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/VarInsnNode.java
@@ -0,0 +1,81 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree; + +import org.objectweb.asm.MethodVisitor; + +/** + * A node that represents a local variable instruction. A local variable + * instruction is an instruction that loads or stores the value of a local + * variable. + * + * @author Eric Bruneton + */ +public class VarInsnNode extends AbstractInsnNode { + + /** + * The operand of this instruction. This operand is the index of a local + * variable. + */ + public int var; + + /** + * Constructs a new {@link VarInsnNode}. + * + * @param opcode the opcode of the local variable instruction to be + * constructed. This opcode must be ILOAD, LLOAD, FLOAD, DLOAD, + * ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var the operand of the instruction to be constructed. This operand + * is the index of a local variable. + */ + public VarInsnNode(final int opcode, final int var) { + super(opcode); + this.var = var; + } + + /** + * Sets the opcode of this instruction. + * + * @param opcode the new instruction opcode. This opcode must be ILOAD, + * LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE + * or RET. + */ + public void setOpcode(final int opcode) { + this.opcode = opcode; + } + + public void accept(final MethodVisitor mv) { + mv.visitVarInsn(opcode, var); + } + + public int getType() { + return VAR_INSN; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Analyzer.java b/asmx/src/org/objectweb/asm/tree/analysis/Analyzer.java new file mode 100644 index 0000000..9fd4028 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/Analyzer.java
@@ -0,0 +1,416 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.IincInsnNode; +import org.objectweb.asm.tree.JumpInsnNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.LookupSwitchInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TableSwitchInsnNode; +import org.objectweb.asm.tree.TryCatchBlockNode; +import org.objectweb.asm.tree.VarInsnNode; + +/** + * A semantic bytecode analyzer. + * + * @author Eric Bruneton + */ +public class Analyzer implements Opcodes { + + private Interpreter interpreter; + + private int n; + + private IntMap indexes; + + private List[] handlers; + + private Frame[] frames; + + private Subroutine[] subroutines; + + private boolean[] queued; + + private int[] queue; + + private int top; + + private boolean jsr; + + /** + * Constructs a new {@link Analyzer}. + * + * @param interpreter the interpreter to be used to symbolically interpret + * the bytecode instructions. + */ + public Analyzer(final Interpreter interpreter) { + this.interpreter = interpreter; + } + + /** + * Analyzes the given method. + * + * @param owner the internal name of the class to which the method belongs. + * @param m the method to be analyzed. + * @return the symbolic state of the execution stack frame at each bytecode + * instruction of the method. The size of the returned array is + * equal to the number of instructions (and labels) of the method. A + * given frame is <tt>null</tt> if and only if the corresponding + * instruction cannot be reached (dead code). + * @throws AnalyzerException if a problem occurs during the analysis. + */ + public Frame[] analyze(final String owner, final MethodNode m) + throws AnalyzerException + { + n = m.instructions.size(); + indexes = new IntMap(2 * n); + handlers = new List[n]; + frames = new Frame[n]; + subroutines = new Subroutine[n]; + queued = new boolean[n]; + queue = new int[n]; + top = 0; + + // computes instruction indexes + for (int i = 0; i < n; ++i) { + Object insn = m.instructions.get(i); + if (insn instanceof LabelNode) { + insn = ((LabelNode) insn).label; + } + indexes.put(insn, i); + } + + // computes exception handlers for each instruction + for (int i = 0; i < m.tryCatchBlocks.size(); ++i) { + TryCatchBlockNode tcb = (TryCatchBlockNode) m.tryCatchBlocks.get(i); + int begin = indexes.get(tcb.start); + int end = indexes.get(tcb.end); + for (int j = begin; j < end; ++j) { + List insnHandlers = handlers[j]; + if (insnHandlers == null) { + insnHandlers = new ArrayList(); + handlers[j] = insnHandlers; + } + insnHandlers.add(tcb); + } + } + + // initializes the data structures for the control flow analysis + // algorithm + Frame current = newFrame(m.maxLocals, m.maxStack); + Frame handler = newFrame(m.maxLocals, m.maxStack); + Type[] args = Type.getArgumentTypes(m.desc); + int local = 0; + if ((m.access & ACC_STATIC) == 0) { + Type ctype = Type.getType("L" + owner + ";"); + current.setLocal(local++, interpreter.newValue(ctype)); + } + for (int i = 0; i < args.length; ++i) { + current.setLocal(local++, interpreter.newValue(args[i])); + if (args[i].getSize() == 2) { + current.setLocal(local++, interpreter.newValue(null)); + } + } + while (local < m.maxLocals) { + current.setLocal(local++, interpreter.newValue(null)); + } + merge(0, current, null); + + // control flow analysis + while (top > 0) { + int insn = queue[--top]; + Frame f = frames[insn]; + Subroutine subroutine = subroutines[insn]; + queued[insn] = false; + + try { + Object o = m.instructions.get(insn); + jsr = false; + + if (o instanceof LabelNode) { + merge(insn + 1, f, subroutine); + } else { + AbstractInsnNode insnNode = (AbstractInsnNode) o; + int insnOpcode = insnNode.getOpcode(); + + current.init(f).execute(insnNode, interpreter); + subroutine = subroutine == null ? null : subroutine.copy(); + + if (insnNode instanceof JumpInsnNode) { + JumpInsnNode j = (JumpInsnNode) insnNode; + if (insnOpcode != GOTO && insnOpcode != JSR) { + merge(insn + 1, current, subroutine); + } + if (insnOpcode == JSR) { + jsr = true; + merge(indexes.get(j.label), + current, + new Subroutine(j.label, m.maxLocals, j)); + } else { + merge(indexes.get(j.label), current, subroutine); + } + } else if (insnNode instanceof LookupSwitchInsnNode) { + LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode; + merge(indexes.get(lsi.dflt), current, subroutine); + for (int j = 0; j < lsi.labels.size(); ++j) { + Label label = (Label) lsi.labels.get(j); + merge(indexes.get(label), current, subroutine); + } + } else if (insnNode instanceof TableSwitchInsnNode) { + TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode; + merge(indexes.get(tsi.dflt), current, subroutine); + for (int j = 0; j < tsi.labels.size(); ++j) { + Label label = (Label) tsi.labels.get(j); + merge(indexes.get(label), current, subroutine); + } + } else if (insnOpcode == RET) { + if (subroutine == null) { + throw new AnalyzerException("RET instruction outside of a sub routine"); + } + for (int i = 0; i < subroutine.callers.size(); ++i) { + int caller = indexes.get(subroutine.callers.get(i)); + merge(caller + 1, + frames[caller], + current, + subroutines[caller], + subroutine.access); + } + } else if (insnOpcode != ATHROW + && (insnOpcode < IRETURN || insnOpcode > RETURN)) + { + if (subroutine != null) { + if (insnNode instanceof VarInsnNode) { + int var = ((VarInsnNode) insnNode).var; + subroutine.access[var] = true; + if (insnOpcode == LLOAD || insnOpcode == DLOAD + || insnOpcode == LSTORE + || insnOpcode == DSTORE) + { + subroutine.access[var + 1] = true; + } + } else if (insnNode instanceof IincInsnNode) { + int var = ((IincInsnNode) insnNode).var; + subroutine.access[var] = true; + } + } + merge(insn + 1, current, subroutine); + } + } + + List insnHandlers = handlers[insn]; + if (insnHandlers != null) { + for (int i = 0; i < insnHandlers.size(); ++i) { + TryCatchBlockNode tcb = (TryCatchBlockNode) insnHandlers.get(i); + Type type; + if (tcb.type == null) { + type = Type.getType("Ljava/lang/Throwable;"); + } else { + type = Type.getType("L" + tcb.type + ";"); + } + handler.init(f); + handler.clearStack(); + handler.push(interpreter.newValue(type)); + merge(indexes.get(tcb.handler), handler, subroutine); + } + } + } catch (AnalyzerException e) { + throw new AnalyzerException("Error at instruction " + insn + + ": " + e.getMessage(), e); + } catch(Exception e) { + throw new AnalyzerException("Error at instruction " + insn + + ": " + e.getMessage(), e); + } + } + + return frames; + } + + /** + * Returns the symbolic stack frame for each instruction of the last + * recently analyzed method. + * + * @return the symbolic state of the execution stack frame at each bytecode + * instruction of the method. The size of the returned array is + * equal to the number of instructions (and labels) of the method. A + * given frame is <tt>null</tt> if the corresponding instruction + * cannot be reached, or if an error occured during the analysis of + * the method. + */ + public Frame[] getFrames() { + return frames; + } + + /** + * Returns the index of the given instruction. + * + * @param insn a {@link Label} or {@link AbstractInsnNode} of the last + * recently analyzed method. + * @return the index of the given instruction of the last recently analyzed + * method. + */ + public int getIndex(final Object insn) { + return indexes.get(insn); + } + + /** + * Returns the exception handlers for the given instruction. + * + * @param insn the index of an instruction of the last recently analyzed + * method. + * @return a list of {@link TryCatchBlockNode} objects. + */ + public List getHandlers(final int insn) { + return handlers[insn]; + } + + /** + * Constructs a new frame with the given size. + * + * @param nLocals the maximum number of local variables of the frame. + * @param nStack the maximum stack size of the frame. + * @return the created frame. + */ + protected Frame newFrame(final int nLocals, final int nStack) { + return new Frame(nLocals, nStack); + } + + /** + * Constructs a new frame that is identical to the given frame. + * + * @param src a frame. + * @return the created frame. + */ + protected Frame newFrame(final Frame src) { + return new Frame(src); + } + + /** + * Creates a control flow graph edge. The default implementation of this + * method does nothing. It can be overriden in order to construct the + * control flow graph of a method (this method is called by the + * {@link #analyze analyze} method during its visit of the method's code). + * + * @param frame the frame corresponding to an instruction. + * @param successor the frame corresponding to a successor instruction. + */ + protected void newControlFlowEdge(final Frame frame, final Frame successor) + { + } + + // ------------------------------------------------------------------------- + + private void merge( + final int insn, + final Frame frame, + final Subroutine subroutine) throws AnalyzerException + { + if (insn > n - 1) { + throw new AnalyzerException("Execution can fall off end of the code"); + } + + Frame oldFrame = frames[insn]; + Subroutine oldSubroutine = subroutines[insn]; + boolean changes = false; + + if (oldFrame == null) { + frames[insn] = newFrame(frame); + changes = true; + } else { + changes |= oldFrame.merge(frame, interpreter); + } + + newControlFlowEdge(frame, oldFrame); + + if (oldSubroutine == null) { + if (subroutine != null) { + subroutines[insn] = subroutine.copy(); + changes = true; + } + } else { + if (subroutine != null) { + changes |= oldSubroutine.merge(subroutine, !jsr); + } + } + if (changes && !queued[insn]) { + queued[insn] = true; + queue[top++] = insn; + } + } + + private void merge( + final int insn, + final Frame beforeJSR, + final Frame afterRET, + final Subroutine subroutineBeforeJSR, + final boolean[] access) throws AnalyzerException + { + if (insn > n - 1) { + throw new AnalyzerException("Execution can fall off end of the code"); + } + + Frame oldFrame = frames[insn]; + Subroutine oldSubroutine = subroutines[insn]; + boolean changes = false; + + afterRET.merge(beforeJSR, access); + + if (oldFrame == null) { + frames[insn] = newFrame(afterRET); + changes = true; + } else { + changes |= oldFrame.merge(afterRET, access); + } + + newControlFlowEdge(afterRET, oldFrame); + + if (oldSubroutine == null) { + if (subroutineBeforeJSR != null) { + subroutines[insn] = subroutineBeforeJSR.copy(); + changes = true; + } + } else { + if (subroutineBeforeJSR != null) { + changes |= oldSubroutine.merge(subroutineBeforeJSR, !jsr); + } + } + if (changes && !queued[insn]) { + queued[insn] = true; + queue[top++] = insn; + } + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/AnalyzerException.java b/asmx/src/org/objectweb/asm/tree/analysis/AnalyzerException.java new file mode 100644 index 0000000..3e0afac --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/AnalyzerException.java
@@ -0,0 +1,56 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +/** + * Thrown if a problem occurs during the analysis of a method. + * + * @author Bing Ran + * @author Eric Bruneton + */ +public class AnalyzerException extends Exception { + + public AnalyzerException(final String msg) { + super(msg); + } + + public AnalyzerException(final String msg, final Throwable exception) { + super(msg, exception); + } + + public AnalyzerException( + final String msg, + final Object expected, + final Value encountered) + { + super((msg == null ? "Expected " : msg + ": expected ") + expected + + ", but found " + encountered); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/BasicInterpreter.java b/asmx/src/org/objectweb/asm/tree/analysis/BasicInterpreter.java new file mode 100644 index 0000000..b92b57c --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/BasicInterpreter.java
@@ -0,0 +1,335 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.List; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.IntInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MultiANewArrayInsnNode; +import org.objectweb.asm.tree.TypeInsnNode; + +/** + * An {@link Interpreter} for {@link BasicValue} values. + * + * @author Eric Bruneton + * @author Bing Ran + */ +public class BasicInterpreter implements Opcodes, Interpreter { + + public Value newValue(final Type type) { + if (type == null) { + return BasicValue.UNINITIALIZED_VALUE; + } + switch (type.getSort()) { + case Type.VOID: + return null; + case Type.BOOLEAN: + case Type.CHAR: + case Type.BYTE: + case Type.SHORT: + case Type.INT: + return BasicValue.INT_VALUE; + case Type.FLOAT: + return BasicValue.FLOAT_VALUE; + case Type.LONG: + return BasicValue.LONG_VALUE; + case Type.DOUBLE: + return BasicValue.DOUBLE_VALUE; + case Type.ARRAY: + case Type.OBJECT: + return BasicValue.REFERENCE_VALUE; + default: + throw new RuntimeException("Internal error."); + } + } + + public Value newOperation(final AbstractInsnNode insn) { + switch (insn.getOpcode()) { + case ACONST_NULL: + return newValue(Type.getType("Lnull;")); + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + return BasicValue.INT_VALUE; + case LCONST_0: + case LCONST_1: + return BasicValue.LONG_VALUE; + case FCONST_0: + case FCONST_1: + case FCONST_2: + return BasicValue.FLOAT_VALUE; + case DCONST_0: + case DCONST_1: + return BasicValue.DOUBLE_VALUE; + case BIPUSH: + case SIPUSH: + return BasicValue.INT_VALUE; + case LDC: + Object cst = ((LdcInsnNode) insn).cst; + if (cst instanceof Integer) { + return BasicValue.INT_VALUE; + } else if (cst instanceof Float) { + return BasicValue.FLOAT_VALUE; + } else if (cst instanceof Long) { + return BasicValue.LONG_VALUE; + } else if (cst instanceof Double) { + return BasicValue.DOUBLE_VALUE; + } else if (cst instanceof Type) { + return newValue(Type.getType("Ljava/lang/Class;")); + } else { + return newValue(Type.getType(cst.getClass())); + } + case JSR: + return BasicValue.RETURNADDRESS_VALUE; + case GETSTATIC: + return newValue(Type.getType(((FieldInsnNode) insn).desc)); + case NEW: + return newValue(Type.getType("L" + ((TypeInsnNode) insn).desc + + ";")); + default: + throw new RuntimeException("Internal error."); + } + } + + public Value copyOperation(final AbstractInsnNode insn, final Value value) + throws AnalyzerException + { + return value; + } + + public Value unaryOperation(final AbstractInsnNode insn, final Value value) + throws AnalyzerException + { + switch (insn.getOpcode()) { + case INEG: + case IINC: + case L2I: + case F2I: + case D2I: + case I2B: + case I2C: + case I2S: + return BasicValue.INT_VALUE; + case FNEG: + case I2F: + case L2F: + case D2F: + return BasicValue.FLOAT_VALUE; + case LNEG: + case I2L: + case F2L: + case D2L: + return BasicValue.LONG_VALUE; + case DNEG: + case I2D: + case L2D: + case F2D: + return BasicValue.DOUBLE_VALUE; + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + case TABLESWITCH: + case LOOKUPSWITCH: + case IRETURN: + case LRETURN: + case FRETURN: + case DRETURN: + case ARETURN: + case PUTSTATIC: + return null; + case GETFIELD: + return newValue(Type.getType(((FieldInsnNode) insn).desc)); + case NEWARRAY: + switch (((IntInsnNode) insn).operand) { + case T_BOOLEAN: + return newValue(Type.getType("[Z")); + case T_CHAR: + return newValue(Type.getType("[C")); + case T_BYTE: + return newValue(Type.getType("[B")); + case T_SHORT: + return newValue(Type.getType("[S")); + case T_INT: + return newValue(Type.getType("[I")); + case T_FLOAT: + return newValue(Type.getType("[F")); + case T_DOUBLE: + return newValue(Type.getType("[D")); + case T_LONG: + return newValue(Type.getType("[J")); + default: + throw new AnalyzerException("Invalid array type"); + } + case ANEWARRAY: + String desc = ((TypeInsnNode) insn).desc; + if (desc.charAt(0) == '[') { + return newValue(Type.getType("[" + desc)); + } else { + return newValue(Type.getType("[L" + desc + ";")); + } + case ARRAYLENGTH: + return BasicValue.INT_VALUE; + case ATHROW: + return null; + case CHECKCAST: + desc = ((TypeInsnNode) insn).desc; + if (desc.charAt(0) == '[') { + return newValue(Type.getType(desc)); + } else { + return newValue(Type.getType("L" + desc + ";")); + } + case INSTANCEOF: + return BasicValue.INT_VALUE; + case MONITORENTER: + case MONITOREXIT: + case IFNULL: + case IFNONNULL: + return null; + default: + throw new RuntimeException("Internal error."); + } + } + + public Value binaryOperation( + final AbstractInsnNode insn, + final Value value1, + final Value value2) throws AnalyzerException + { + switch (insn.getOpcode()) { + case IALOAD: + case BALOAD: + case CALOAD: + case SALOAD: + case IADD: + case ISUB: + case IMUL: + case IDIV: + case IREM: + case ISHL: + case ISHR: + case IUSHR: + case IAND: + case IOR: + case IXOR: + return BasicValue.INT_VALUE; + case FALOAD: + case FADD: + case FSUB: + case FMUL: + case FDIV: + case FREM: + return BasicValue.FLOAT_VALUE; + case LALOAD: + case LADD: + case LSUB: + case LMUL: + case LDIV: + case LREM: + case LSHL: + case LSHR: + case LUSHR: + case LAND: + case LOR: + case LXOR: + return BasicValue.LONG_VALUE; + case DALOAD: + case DADD: + case DSUB: + case DMUL: + case DDIV: + case DREM: + return BasicValue.DOUBLE_VALUE; + case AALOAD: + Type t = ((BasicValue) value1).getType(); + if (t != null && t.getSort() == Type.ARRAY) { + return newValue(t.getElementType()); + } else { + return BasicValue.REFERENCE_VALUE; + } + case LCMP: + case FCMPL: + case FCMPG: + case DCMPL: + case DCMPG: + return BasicValue.INT_VALUE; + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ACMPEQ: + case IF_ACMPNE: + case PUTFIELD: + return null; + default: + throw new RuntimeException("Internal error."); + } + } + + public Value ternaryOperation( + final AbstractInsnNode insn, + final Value value1, + final Value value2, + final Value value3) throws AnalyzerException + { + return null; + } + + public Value naryOperation(final AbstractInsnNode insn, final List values) + throws AnalyzerException + { + if (insn.getOpcode() == MULTIANEWARRAY) { + return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc)); + } else { + return newValue(Type.getReturnType(((MethodInsnNode) insn).desc)); + } + } + + public Value merge(final Value v, final Value w) { + if (!v.equals(w)) { + return BasicValue.UNINITIALIZED_VALUE; + } + return v; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/BasicValue.java b/asmx/src/org/objectweb/asm/tree/analysis/BasicValue.java new file mode 100644 index 0000000..19cdb33 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/BasicValue.java
@@ -0,0 +1,105 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import org.objectweb.asm.Type; + +/** + * A {@link Value} that is represented by its type in a seven types type sytem. + * This type system distinguishes the UNINITIALZED, INT, FLOAT, LONG, DOUBLE, + * REFERENCE and RETURNADDRESS types. + * + * @author Eric Bruneton + */ +public class BasicValue implements Value { + + public final static Value UNINITIALIZED_VALUE = new BasicValue(null); + + public final static Value INT_VALUE = new BasicValue(Type.INT_TYPE); + + public final static Value FLOAT_VALUE = new BasicValue(Type.FLOAT_TYPE); + + public final static Value LONG_VALUE = new BasicValue(Type.LONG_TYPE); + + public final static Value DOUBLE_VALUE = new BasicValue(Type.DOUBLE_TYPE); + + public final static Value REFERENCE_VALUE = new BasicValue(Type.getType("Ljava/lang/Object;")); + + public final static Value RETURNADDRESS_VALUE = new BasicValue(null); + + private Type type; + + public BasicValue(final Type type) { + this.type = type; + } + + public Type getType() { + return type; + } + + public int getSize() { + return type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE ? 2 : 1; + } + + public boolean isReference() { + return type != null + && (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY); + } + + public boolean equals(final Object value) { + if (value == this) { + return true; + } else if (value instanceof BasicValue) { + if (type == null) { + return ((BasicValue) value).type == null; + } else { + return type.equals(((BasicValue) value).type); + } + } else { + return false; + } + } + + public int hashCode() { + return type == null ? 0 : type.hashCode(); + } + + public String toString() { + if (this == UNINITIALIZED_VALUE) { + return "."; + } else if (this == RETURNADDRESS_VALUE) { + return "A"; + } else if (this == REFERENCE_VALUE) { + return "R"; + } else { + return type.getDescriptor(); + } + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/BasicVerifier.java b/asmx/src/org/objectweb/asm/tree/analysis/BasicVerifier.java new file mode 100644 index 0000000..0a797d4 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/BasicVerifier.java
@@ -0,0 +1,428 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.List; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; + +/** + * An extended {@link BasicInterpreter} that checks that bytecode instructions + * are correctly used. + * + * @author Eric Bruneton + * @author Bing Ran + */ +public class BasicVerifier extends BasicInterpreter { + + public Value copyOperation(final AbstractInsnNode insn, final Value value) + throws AnalyzerException + { + Value expected; + switch (insn.getOpcode()) { + case ILOAD: + case ISTORE: + expected = BasicValue.INT_VALUE; + break; + case FLOAD: + case FSTORE: + expected = BasicValue.FLOAT_VALUE; + break; + case LLOAD: + case LSTORE: + expected = BasicValue.LONG_VALUE; + break; + case DLOAD: + case DSTORE: + expected = BasicValue.DOUBLE_VALUE; + break; + case ALOAD: + if (!((BasicValue) value).isReference()) { + throw new AnalyzerException(null, + "an object reference", + value); + } + return value; + case ASTORE: + if (!((BasicValue) value).isReference() + && value != BasicValue.RETURNADDRESS_VALUE) + { + throw new AnalyzerException(null, + "an object reference or a return address", + value); + } + return value; + default: + return value; + } + // type is necessarily a primitive type here, + // so value must be == to expected value + if (value != expected) { + throw new AnalyzerException(null, expected, value); + } + return value; + } + + public Value unaryOperation(final AbstractInsnNode insn, Value value) + throws AnalyzerException + { + Value expected; + switch (insn.getOpcode()) { + case INEG: + case IINC: + case I2F: + case I2L: + case I2D: + case I2B: + case I2C: + case I2S: + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + case TABLESWITCH: + case LOOKUPSWITCH: + case IRETURN: + case NEWARRAY: + case ANEWARRAY: + expected = BasicValue.INT_VALUE; + break; + case FNEG: + case F2I: + case F2L: + case F2D: + case FRETURN: + expected = BasicValue.FLOAT_VALUE; + break; + case LNEG: + case L2I: + case L2F: + case L2D: + case LRETURN: + expected = BasicValue.LONG_VALUE; + break; + case DNEG: + case D2I: + case D2F: + case D2L: + case DRETURN: + expected = BasicValue.DOUBLE_VALUE; + break; + case GETFIELD: + expected = newValue(Type.getType("L" + + ((FieldInsnNode) insn).owner + ";")); + break; + case CHECKCAST: + if (!((BasicValue) value).isReference()) { + throw new AnalyzerException(null, + "an object reference", + value); + } + return super.unaryOperation(insn, value); + case ARRAYLENGTH: + if (!isArrayValue(value)) { + throw new AnalyzerException(null, + "an array reference", + value); + } + return super.unaryOperation(insn, value); + case ARETURN: + case ATHROW: + case INSTANCEOF: + case MONITORENTER: + case MONITOREXIT: + case IFNULL: + case IFNONNULL: + if (!((BasicValue) value).isReference()) { + throw new AnalyzerException(null, + "an object reference", + value); + } + return super.unaryOperation(insn, value); + case PUTSTATIC: + expected = newValue(Type.getType(((FieldInsnNode) insn).desc)); + break; + default: + throw new RuntimeException("Internal error."); + } + if (!isSubTypeOf(value, expected)) { + throw new AnalyzerException(null, expected, value); + } + return super.unaryOperation(insn, value); + } + + public Value binaryOperation( + final AbstractInsnNode insn, + final Value value1, + final Value value2) throws AnalyzerException + { + Value expected1; + Value expected2; + switch (insn.getOpcode()) { + case IALOAD: + expected1 = newValue(Type.getType("[I")); + expected2 = BasicValue.INT_VALUE; + break; + case BALOAD: + if (!isSubTypeOf(value1, newValue(Type.getType("[Z")))) { + expected1 = newValue(Type.getType("[B")); + } else { + expected1 = newValue(Type.getType("[Z")); + } + expected2 = BasicValue.INT_VALUE; + break; + case CALOAD: + expected1 = newValue(Type.getType("[C")); + expected2 = BasicValue.INT_VALUE; + break; + case SALOAD: + expected1 = newValue(Type.getType("[S")); + expected2 = BasicValue.INT_VALUE; + break; + case LALOAD: + expected1 = newValue(Type.getType("[J")); + expected2 = BasicValue.INT_VALUE; + break; + case FALOAD: + expected1 = newValue(Type.getType("[F")); + expected2 = BasicValue.INT_VALUE; + break; + case DALOAD: + expected1 = newValue(Type.getType("[D")); + expected2 = BasicValue.INT_VALUE; + break; + case AALOAD: + expected1 = newValue(Type.getType("[Ljava/lang/Object;")); + expected2 = BasicValue.INT_VALUE; + break; + case IADD: + case ISUB: + case IMUL: + case IDIV: + case IREM: + case ISHL: + case ISHR: + case IUSHR: + case IAND: + case IOR: + case IXOR: + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + expected1 = BasicValue.INT_VALUE; + expected2 = BasicValue.INT_VALUE; + break; + case FADD: + case FSUB: + case FMUL: + case FDIV: + case FREM: + case FCMPL: + case FCMPG: + expected1 = BasicValue.FLOAT_VALUE; + expected2 = BasicValue.FLOAT_VALUE; + break; + case LADD: + case LSUB: + case LMUL: + case LDIV: + case LREM: + case LAND: + case LOR: + case LXOR: + case LCMP: + expected1 = BasicValue.LONG_VALUE; + expected2 = BasicValue.LONG_VALUE; + break; + case LSHL: + case LSHR: + case LUSHR: + expected1 = BasicValue.LONG_VALUE; + expected2 = BasicValue.INT_VALUE; + break; + case DADD: + case DSUB: + case DMUL: + case DDIV: + case DREM: + case DCMPL: + case DCMPG: + expected1 = BasicValue.DOUBLE_VALUE; + expected2 = BasicValue.DOUBLE_VALUE; + break; + case IF_ACMPEQ: + case IF_ACMPNE: + expected1 = BasicValue.REFERENCE_VALUE; + expected2 = BasicValue.REFERENCE_VALUE; + break; + case PUTFIELD: + FieldInsnNode fin = (FieldInsnNode) insn; + expected1 = newValue(Type.getType("L" + fin.owner + ";")); + expected2 = newValue(Type.getType(fin.desc)); + break; + default: + throw new RuntimeException("Internal error."); + } + if (!isSubTypeOf(value1, expected1)) { + throw new AnalyzerException("First argument", expected1, value1); + } else if (!isSubTypeOf(value2, expected2)) { + throw new AnalyzerException("Second argument", expected2, value2); + } + if (insn.getOpcode() == AALOAD) { + return getElementValue(value1); + } else { + return super.binaryOperation(insn, value1, value2); + } + } + + public Value ternaryOperation( + final AbstractInsnNode insn, + final Value value1, + final Value value2, + final Value value3) throws AnalyzerException + { + Value expected1; + Value expected3; + switch (insn.getOpcode()) { + case IASTORE: + expected1 = newValue(Type.getType("[I")); + expected3 = BasicValue.INT_VALUE; + break; + case BASTORE: + if (!isSubTypeOf(value1, newValue(Type.getType("[Z")))) { + expected1 = newValue(Type.getType("[B")); + } else { + expected1 = newValue(Type.getType("[Z")); + } + expected3 = BasicValue.INT_VALUE; + break; + case CASTORE: + expected1 = newValue(Type.getType("[C")); + expected3 = BasicValue.INT_VALUE; + break; + case SASTORE: + expected1 = newValue(Type.getType("[S")); + expected3 = BasicValue.INT_VALUE; + break; + case LASTORE: + expected1 = newValue(Type.getType("[J")); + expected3 = BasicValue.LONG_VALUE; + break; + case FASTORE: + expected1 = newValue(Type.getType("[F")); + expected3 = BasicValue.FLOAT_VALUE; + break; + case DASTORE: + expected1 = newValue(Type.getType("[D")); + expected3 = BasicValue.DOUBLE_VALUE; + break; + case AASTORE: + expected1 = value1; + expected3 = BasicValue.REFERENCE_VALUE; + break; + default: + throw new RuntimeException("Internal error."); + } + if (!isSubTypeOf(value1, expected1)) { + throw new AnalyzerException("First argument", "a " + expected1 + + " array reference", value1); + } else if (value2 != BasicValue.INT_VALUE) { + throw new AnalyzerException("Second argument", + BasicValue.INT_VALUE, + value2); + } else if (!isSubTypeOf(value3, expected3)) { + throw new AnalyzerException("Third argument", expected3, value3); + } + return null; + } + + public Value naryOperation(final AbstractInsnNode insn, final List values) + throws AnalyzerException + { + int opcode = insn.getOpcode(); + if (opcode == MULTIANEWARRAY) { + for (int i = 0; i < values.size(); ++i) { + if (values.get(i) != BasicValue.INT_VALUE) { + throw new AnalyzerException(null, + BasicValue.INT_VALUE, + (Value) values.get(i)); + } + } + } else { + int i = 0; + int j = 0; + if (opcode != INVOKESTATIC) { + String own = ((MethodInsnNode) insn).owner; + if (own.charAt(0) != '[') { // can happen with JDK1.5 clone() + own = "L" + own + ";"; + } + Type owner = Type.getType(own); + if (!isSubTypeOf((Value) values.get(i++), newValue(owner))) { + throw new AnalyzerException("Method owner", + newValue(owner), + (Value) values.get(0)); + } + } + Type[] args = Type.getArgumentTypes(((MethodInsnNode) insn).desc); + while (i < values.size()) { + Value expected = newValue(args[j++]); + Value encountered = (Value) values.get(i++); + if (!isSubTypeOf(encountered, expected)) { + throw new AnalyzerException("Argument " + j, + expected, + encountered); + } + } + } + return super.naryOperation(insn, values); + } + + protected boolean isArrayValue(final Value value) { + return ((BasicValue) value).isReference(); + } + + protected Value getElementValue(final Value objectArrayValue) + throws AnalyzerException + { + return BasicValue.REFERENCE_VALUE; + } + + protected boolean isSubTypeOf(final Value value, final Value expected) { + return value == expected; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/DataflowInterpreter.java b/asmx/src/org/objectweb/asm/tree/analysis/DataflowInterpreter.java new file mode 100644 index 0000000..07edd74 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/DataflowInterpreter.java
@@ -0,0 +1,174 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; + +/** + * An {@link Interpreter} for {@link DataflowValue} values. + * + * @author Eric Bruneton + */ +public class DataflowInterpreter implements Opcodes, Interpreter { + + public Value newValue(final Type type) { + return new DataflowValue(type == null ? 1 : type.getSize()); + } + + public Value newOperation(final AbstractInsnNode insn) { + int size; + switch (insn.getOpcode()) { + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + size = 2; + break; + case LDC: + Object cst = ((LdcInsnNode) insn).cst; + size = cst instanceof Long || cst instanceof Double ? 2 : 1; + break; + case GETSTATIC: + size = Type.getType(((FieldInsnNode) insn).desc).getSize(); + break; + default: + size = 1; + } + return new DataflowValue(size, insn); + } + + public Value copyOperation(final AbstractInsnNode insn, final Value value) { + return new DataflowValue(value.getSize(), insn); + } + + public Value unaryOperation(final AbstractInsnNode insn, final Value value) + { + int size; + switch (insn.getOpcode()) { + case LNEG: + case DNEG: + case I2L: + case I2D: + case L2D: + case F2L: + case F2D: + case D2L: + size = 2; + break; + case GETFIELD: + size = Type.getType(((FieldInsnNode) insn).desc).getSize(); + break; + default: + size = 1; + } + return new DataflowValue(size, insn); + } + + public Value binaryOperation( + final AbstractInsnNode insn, + final Value value1, + final Value value2) + { + int size; + switch (insn.getOpcode()) { + case LALOAD: + case DALOAD: + case LADD: + case DADD: + case LSUB: + case DSUB: + case LMUL: + case DMUL: + case LDIV: + case DDIV: + case LREM: + case DREM: + case LSHL: + case LSHR: + case LUSHR: + case LAND: + case LOR: + case LXOR: + size = 2; + break; + default: + size = 1; + } + return new DataflowValue(size, insn); + } + + public Value ternaryOperation( + final AbstractInsnNode insn, + final Value value1, + final Value value2, + final Value value3) + { + return new DataflowValue(1, insn); + } + + public Value naryOperation(final AbstractInsnNode insn, final List values) { + int size; + if (insn.getOpcode() == MULTIANEWARRAY) { + size = 1; + } else { + size = Type.getReturnType(((MethodInsnNode) insn).desc).getSize(); + } + return new DataflowValue(size, insn); + } + + public Value merge(final Value v, final Value w) { + DataflowValue dv = (DataflowValue) v; + DataflowValue dw = (DataflowValue) w; + if (dv.insns instanceof SmallSet && dw.insns instanceof SmallSet) { + Set s = ((SmallSet) dv.insns).union((SmallSet) dw.insns); + if (s == dv.insns && dv.size == dw.size) { + return v; + } else { + return new DataflowValue(Math.min(dv.size, dw.size), s); + } + } + if (dv.size != dw.size || !dv.insns.containsAll(dw.insns)) { + Set s = new HashSet(); + s.addAll(dv.insns); + s.addAll(dw.insns); + return new DataflowValue(Math.min(dv.size, dw.size), s); + } + return v; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/DataflowValue.java b/asmx/src/org/objectweb/asm/tree/analysis/DataflowValue.java new file mode 100644 index 0000000..0019d7f --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/DataflowValue.java
@@ -0,0 +1,92 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.Set; + +import org.objectweb.asm.tree.AbstractInsnNode; + +/** + * A {@link Value} that is represented by its type in a two types type system. + * This type system distinguishes the ONEWORD and TWOWORDS types. + * + * @author Eric Bruneton + */ +public class DataflowValue implements Value { + + /** + * The size of this value. + */ + public final int size; + + /** + * The instructions that can produce this value. For example, for the Java + * code below, the instructions that can produce the value of <tt>i</tt> + * at line 5 are the txo ISTORE instructions at line 1 and 3: + * + * <pre> + * 1: i = 0; + * 2: if (...) { + * 3: i = 1; + * 4: } + * 5: return i; + * </pre> + * + * This field is a set of {@link AbstractInsnNode} objects. + */ + public final Set insns; + + public DataflowValue(final int size) { + this(size, SmallSet.EMPTY_SET); + } + + public DataflowValue(final int size, final AbstractInsnNode insn) { + this.size = size; + this.insns = new SmallSet(insn, null); + } + + public DataflowValue(final int size, final Set insns) { + this.size = size; + this.insns = insns; + } + + public int getSize() { + return size; + } + + public boolean equals(final Object value) { + DataflowValue v = (DataflowValue) value; + return size == v.size && insns.equals(v.insns); + } + + public int hashCode() { + return insns.hashCode(); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Frame.java b/asmx/src/org/objectweb/asm/tree/analysis/Frame.java new file mode 100644 index 0000000..bc2d873 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/Frame.java
@@ -0,0 +1,670 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.IincInsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MultiANewArrayInsnNode; +import org.objectweb.asm.tree.VarInsnNode; + +/** + * A symbolic execution stack frame. A stack frame contains a set of local + * variable slots, and an operand stack. Warning: long and double values are + * represented by <i>two</i> slots in local variables, and by <i>one</i> slot + * in the operand stack. + * + * @author Eric Bruneton + */ +public class Frame { + + /** + * The local variables and operand stack of this frame. + */ + private Value[] values; + + /** + * The number of local variables of this frame. + */ + private int locals; + + /** + * The number of elements in the operand stack. + */ + private int top; + + /** + * Constructs a new frame with the given size. + * + * @param nLocals the maximum number of local variables of the frame. + * @param nStack the maximum stack size of the frame. + */ + public Frame(final int nLocals, final int nStack) { + this.values = new Value[nLocals + nStack]; + this.locals = nLocals; + } + + /** + * Constructs a new frame that is identical to the given frame. + * + * @param src a frame. + */ + public Frame(final Frame src) { + this(src.locals, src.values.length - src.locals); + init(src); + } + + /** + * Copies the state of the given frame into this frame. + * + * @param src a frame. + * @return this frame. + */ + public Frame init(final Frame src) { + System.arraycopy(src.values, 0, values, 0, values.length); + top = src.top; + return this; + } + + /** + * Returns the maximum number of local variables of this frame. + * + * @return the maximum number of local variables of this frame. + */ + public int getLocals() { + return locals; + } + + /** + * Returns the value of the given local variable. + * + * @param i a local variable index. + * @return the value of the given local variable. + * @throws IndexOutOfBoundsException if the variable does not exist. + */ + public Value getLocal(final int i) throws IndexOutOfBoundsException { + if (i >= locals) { + throw new IndexOutOfBoundsException("Trying to access an inexistant local variable"); + } + return values[i]; + } + + /** + * Sets the value of the given local variable. + * + * @param i a local variable index. + * @param value the new value of this local variable. + * @throws IndexOutOfBoundsException if the variable does not exist. + */ + public void setLocal(final int i, final Value value) + throws IndexOutOfBoundsException + { + if (i >= locals) { + throw new IndexOutOfBoundsException("Trying to access an inexistant local variable"); + } + values[i] = value; + } + + /** + * Returns the number of values in the operand stack of this frame. Long and + * double values are treated as single values. + * + * @return the number of values in the operand stack of this frame. + */ + public int getStackSize() { + return top; + } + + /** + * Returns the value of the given operand stack slot. + * + * @param i the index of an operand stack slot. + * @return the value of the given operand stack slot. + * @throws IndexOutOfBoundsException if the operand stack slot does not + * exist. + */ + public Value getStack(final int i) throws IndexOutOfBoundsException { + if (i >= top) { + throw new IndexOutOfBoundsException("Trying to access an inexistant stack element"); + } + return values[i + locals]; + } + + /** + * Clears the operand stack of this frame. + */ + public void clearStack() { + top = 0; + } + + /** + * Pops a value from the operand stack of this frame. + * + * @return the value that has been popped from the stack. + * @throws IndexOutOfBoundsException if the operand stack is empty. + */ + public Value pop() throws IndexOutOfBoundsException { + if (top == 0) { + throw new IndexOutOfBoundsException("Cannot pop operand off an empty stack."); + } + return values[--top + locals]; + } + + /** + * Pushes a value into the operand stack of this frame. + * + * @param value the value that must be pushed into the stack. + * @throws IndexOutOfBoundsException if the operand stack is full. + */ + public void push(final Value value) throws IndexOutOfBoundsException { + if (top + locals >= values.length) { + throw new IndexOutOfBoundsException("Insufficient maximum stack size."); + } + values[top++ + locals] = value; + } + + public void execute( + final AbstractInsnNode insn, + final Interpreter interpreter) throws AnalyzerException + { + Value value1, value2, value3, value4; + List values; + int var; + + switch (insn.getOpcode()) { + case Opcodes.NOP: + break; + case Opcodes.ACONST_NULL: + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.BIPUSH: + case Opcodes.SIPUSH: + case Opcodes.LDC: + push(interpreter.newOperation(insn)); + break; + case Opcodes.ILOAD: + case Opcodes.LLOAD: + case Opcodes.FLOAD: + case Opcodes.DLOAD: + case Opcodes.ALOAD: + push(interpreter.copyOperation(insn, + getLocal(((VarInsnNode) insn).var))); + break; + case Opcodes.IALOAD: + case Opcodes.LALOAD: + case Opcodes.FALOAD: + case Opcodes.DALOAD: + case Opcodes.AALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + value2 = pop(); + value1 = pop(); + push(interpreter.binaryOperation(insn, value1, value2)); + break; + case Opcodes.ISTORE: + case Opcodes.LSTORE: + case Opcodes.FSTORE: + case Opcodes.DSTORE: + case Opcodes.ASTORE: + value1 = interpreter.copyOperation(insn, pop()); + var = ((VarInsnNode) insn).var; + setLocal(var, value1); + if (value1.getSize() == 2) { + setLocal(var + 1, interpreter.newValue(null)); + } + if (var > 0) { + Value local = getLocal(var - 1); + if (local != null && local.getSize() == 2) { + setLocal(var + 1, interpreter.newValue(null)); + } + } + break; + case Opcodes.IASTORE: + case Opcodes.LASTORE: + case Opcodes.FASTORE: + case Opcodes.DASTORE: + case Opcodes.AASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + value3 = pop(); + value2 = pop(); + value1 = pop(); + interpreter.ternaryOperation(insn, value1, value2, value3); + break; + case Opcodes.POP: + if (pop().getSize() == 2) { + throw new AnalyzerException("Illegal use of POP"); + } + break; + case Opcodes.POP2: + if (pop().getSize() == 1) { + if (pop().getSize() != 1) { + throw new AnalyzerException("Illegal use of POP2"); + } + } + break; + case Opcodes.DUP: + value1 = pop(); + if (value1.getSize() != 1) { + throw new AnalyzerException("Illegal use of DUP"); + } + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value1)); + break; + case Opcodes.DUP_X1: + value1 = pop(); + value2 = pop(); + if (value1.getSize() != 1 || value2.getSize() != 1) { + throw new AnalyzerException("Illegal use of DUP_X1"); + } + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + case Opcodes.DUP_X2: + value1 = pop(); + if (value1.getSize() == 1) { + value2 = pop(); + if (value2.getSize() == 1) { + value3 = pop(); + if (value3.getSize() == 1) { + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value3)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } else { + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } + throw new AnalyzerException("Illegal use of DUP_X2"); + case Opcodes.DUP2: + value1 = pop(); + if (value1.getSize() == 1) { + value2 = pop(); + if (value2.getSize() == 1) { + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } else { + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value1)); + break; + } + throw new AnalyzerException("Illegal use of DUP2"); + case Opcodes.DUP2_X1: + value1 = pop(); + if (value1.getSize() == 1) { + value2 = pop(); + if (value2.getSize() == 1) { + value3 = pop(); + if (value3.getSize() == 1) { + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value3)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } + } else { + value2 = pop(); + if (value2.getSize() == 1) { + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } + throw new AnalyzerException("Illegal use of DUP2_X1"); + case Opcodes.DUP2_X2: + value1 = pop(); + if (value1.getSize() == 1) { + value2 = pop(); + if (value2.getSize() == 1) { + value3 = pop(); + if (value3.getSize() == 1) { + value4 = pop(); + if (value4.getSize() == 1) { + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value4)); + push(interpreter.copyOperation(insn, value3)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } else { + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value3)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } + } else { + value2 = pop(); + if (value2.getSize() == 1) { + value3 = pop(); + if (value3.getSize() == 1) { + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value3)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } else { + push(interpreter.copyOperation(insn, value1)); + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + } + } + throw new AnalyzerException("Illegal use of DUP2_X2"); + case Opcodes.SWAP: + value2 = pop(); + value1 = pop(); + if (value1.getSize() != 1 || value2.getSize() != 1) { + throw new AnalyzerException("Illegal use of SWAP"); + } + push(interpreter.copyOperation(insn, value2)); + push(interpreter.copyOperation(insn, value1)); + break; + case Opcodes.IADD: + case Opcodes.LADD: + case Opcodes.FADD: + case Opcodes.DADD: + case Opcodes.ISUB: + case Opcodes.LSUB: + case Opcodes.FSUB: + case Opcodes.DSUB: + case Opcodes.IMUL: + case Opcodes.LMUL: + case Opcodes.FMUL: + case Opcodes.DMUL: + case Opcodes.IDIV: + case Opcodes.LDIV: + case Opcodes.FDIV: + case Opcodes.DDIV: + case Opcodes.IREM: + case Opcodes.LREM: + case Opcodes.FREM: + case Opcodes.DREM: + value2 = pop(); + value1 = pop(); + push(interpreter.binaryOperation(insn, value1, value2)); + break; + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + push(interpreter.unaryOperation(insn, pop())); + break; + case Opcodes.ISHL: + case Opcodes.LSHL: + case Opcodes.ISHR: + case Opcodes.LSHR: + case Opcodes.IUSHR: + case Opcodes.LUSHR: + case Opcodes.IAND: + case Opcodes.LAND: + case Opcodes.IOR: + case Opcodes.LOR: + case Opcodes.IXOR: + case Opcodes.LXOR: + value2 = pop(); + value1 = pop(); + push(interpreter.binaryOperation(insn, value1, value2)); + break; + case Opcodes.IINC: + var = ((IincInsnNode) insn).var; + setLocal(var, interpreter.unaryOperation(insn, getLocal(var))); + break; + case Opcodes.I2L: + case Opcodes.I2F: + case Opcodes.I2D: + case Opcodes.L2I: + case Opcodes.L2F: + case Opcodes.L2D: + case Opcodes.F2I: + case Opcodes.F2L: + case Opcodes.F2D: + case Opcodes.D2I: + case Opcodes.D2L: + case Opcodes.D2F: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + push(interpreter.unaryOperation(insn, pop())); + break; + case Opcodes.LCMP: + case Opcodes.FCMPL: + case Opcodes.FCMPG: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + value2 = pop(); + value1 = pop(); + push(interpreter.binaryOperation(insn, value1, value2)); + break; + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + interpreter.unaryOperation(insn, pop()); + break; + case Opcodes.IF_ICMPEQ: + case Opcodes.IF_ICMPNE: + case Opcodes.IF_ICMPLT: + case Opcodes.IF_ICMPGE: + case Opcodes.IF_ICMPGT: + case Opcodes.IF_ICMPLE: + case Opcodes.IF_ACMPEQ: + case Opcodes.IF_ACMPNE: + value2 = pop(); + value1 = pop(); + interpreter.binaryOperation(insn, value1, value2); + break; + case Opcodes.GOTO: + break; + case Opcodes.JSR: + push(interpreter.newOperation(insn)); + break; + case Opcodes.RET: + break; + case Opcodes.TABLESWITCH: + case Opcodes.LOOKUPSWITCH: + case Opcodes.IRETURN: + case Opcodes.LRETURN: + case Opcodes.FRETURN: + case Opcodes.DRETURN: + case Opcodes.ARETURN: + interpreter.unaryOperation(insn, pop()); + break; + case Opcodes.RETURN: + break; + case Opcodes.GETSTATIC: + push(interpreter.newOperation(insn)); + break; + case Opcodes.PUTSTATIC: + interpreter.unaryOperation(insn, pop()); + break; + case Opcodes.GETFIELD: + push(interpreter.unaryOperation(insn, pop())); + break; + case Opcodes.PUTFIELD: + value2 = pop(); + value1 = pop(); + interpreter.binaryOperation(insn, value1, value2); + break; + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKESPECIAL: + case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEINTERFACE: + values = new ArrayList(); + String desc = ((MethodInsnNode) insn).desc; + for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) { + values.add(0, pop()); + } + if (insn.getOpcode() != Opcodes.INVOKESTATIC) { + values.add(0, pop()); + } + if (Type.getReturnType(desc) == Type.VOID_TYPE) { + interpreter.naryOperation(insn, values); + } else { + push(interpreter.naryOperation(insn, values)); + } + break; + case Opcodes.NEW: + push(interpreter.newOperation(insn)); + break; + case Opcodes.NEWARRAY: + case Opcodes.ANEWARRAY: + case Opcodes.ARRAYLENGTH: + push(interpreter.unaryOperation(insn, pop())); + break; + case Opcodes.ATHROW: + interpreter.unaryOperation(insn, pop()); + break; + case Opcodes.CHECKCAST: + case Opcodes.INSTANCEOF: + push(interpreter.unaryOperation(insn, pop())); + break; + case Opcodes.MONITORENTER: + case Opcodes.MONITOREXIT: + interpreter.unaryOperation(insn, pop()); + break; + case Opcodes.MULTIANEWARRAY: + values = new ArrayList(); + for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) { + values.add(0, pop()); + } + push(interpreter.naryOperation(insn, values)); + break; + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: + interpreter.unaryOperation(insn, pop()); + break; + default: + throw new RuntimeException("Illegal opcode"); + } + } + + /** + * Merges this frame with the given frame. + * + * @param frame a frame. + * @param interpreter the interpreter used to merge values. + * @return <tt>true</tt> if this frame has been changed as a result of the + * merge operation, or <tt>false</tt> otherwise. + * @throws AnalyzerException if the frames have incompatible sizes. + */ + public boolean merge(final Frame frame, final Interpreter interpreter) + throws AnalyzerException + { + if (top != frame.top) { + throw new AnalyzerException("Incompatible stack heights"); + } + boolean changes = false; + for (int i = 0; i < locals + top; ++i) { + Value v = interpreter.merge(values[i], frame.values[i]); + if (v != values[i]) { + values[i] = v; + changes |= true; + } + } + return changes; + } + + /** + * Merges this frame with the given frame (case of a RET instruction). + * + * @param frame a frame + * @param access the local variables that have been accessed by the + * subroutine to which the RET instruction corresponds. + * @return <tt>true</tt> if this frame has been changed as a result of the + * merge operation, or <tt>false</tt> otherwise. + */ + public boolean merge(final Frame frame, final boolean[] access) { + boolean changes = false; + for (int i = 0; i < locals; ++i) { + if (!access[i] && !values[i].equals(frame.values[i])) { + values[i] = frame.values[i]; + changes = true; + } + } + return changes; + } + + /** + * Returns a string representation of this frame. + * + * @return a string representation of this frame. + */ + public String toString() { + StringBuffer b = new StringBuffer(); + for (int i = 0; i < locals; ++i) { + b.append(values[i]).append(' '); + } + b.append(' '); + for (int i = 0; i < top; ++i) { + b.append(values[i + locals].toString()).append(' '); + } + return b.toString(); + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/IntMap.java b/asmx/src/org/objectweb/asm/tree/analysis/IntMap.java new file mode 100644 index 0000000..9528482 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/IntMap.java
@@ -0,0 +1,73 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +/** + * A fixed size map of integer values. + * + * @author Eric Bruneton + */ +class IntMap { + + private int size; + + private Object[] keys; + + private int[] values; + + public IntMap(final int size) { + this.size = size; + this.keys = new Object[size]; + this.values = new int[size]; + } + + public int get(final Object key) { + int n = size; + int h = (key.hashCode() & 0x7FFFFFFF) % n; + int i = h; + while (keys[i] != key) { + i = (i + 1) % n; + if (i == h) { + throw new RuntimeException("Cannot find index of " + key); + } + } + return values[i]; + } + + public void put(final Object key, final int value) { + int n = size; + int i = (key.hashCode() & 0x7FFFFFFF) % n; + while (keys[i] != null) { + i = (i + 1) % n; + } + keys[i] = key; + values[i] = value; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Interpreter.java b/asmx/src/org/objectweb/asm/tree/analysis/Interpreter.java new file mode 100644 index 0000000..f7a2f53 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/Interpreter.java
@@ -0,0 +1,178 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.List; + +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; + +/** + * A semantic bytecode interpreter. More precisely, this interpreter only + * manages the computation of values from other values: it does not manage the + * transfer of values to or from the stack, and to or from the local variables. + * This separation allows a generic bytecode {@link Analyzer} to work with + * various semantic interpreters, without needing to duplicate the code to + * simulate the transfer of values. + * + * @author Eric Bruneton + */ +public interface Interpreter { + + /** + * Creates a new value that represents the given type. + * + * Called for method parameters (including <code>this</code>), + * exception handler variable and with <code>null</code> type + * for variables reserved by long and double types. + * + * @param type a primitive or reference type, or <tt>null</tt> to + * represent an uninitialized value. + * @return a value that represents the given type. The size of the returned + * value must be equal to the size of the given type. + */ + Value newValue(Type type); + + /** + * Interprets a bytecode instruction without arguments. This method is + * called for the following opcodes: + * + * 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, BIPUSH, SIPUSH, LDC, JSR, GETSTATIC, NEW + * + * @param insn the bytecode instruction to be interpreted. + * @return the result of the interpretation of the given instruction. + * @throws AnalyzerException if an error occured during the interpretation. + */ + Value newOperation(AbstractInsnNode insn) throws AnalyzerException; + + /** + * Interprets a bytecode instruction that moves a value on the stack or to + * or from local variables. This method is called for the following opcodes: + * + * ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, + * ASTORE, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP + * + * @param insn the bytecode instruction to be interpreted. + * @param value the value that must be moved by the instruction. + * @return the result of the interpretation of the given instruction. The + * returned value must be <tt>equal</tt> to the given value. + * @throws AnalyzerException if an error occured during the interpretation. + */ + Value copyOperation(AbstractInsnNode insn, Value value) + throws AnalyzerException; + + /** + * Interprets a bytecode instruction with a single argument. This method is + * called for the following opcodes: + * + * INEG, LNEG, FNEG, DNEG, IINC, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, + * F2D, D2I, D2L, D2F, I2B, I2C, I2S, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, + * TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, + * PUTSTATIC, GETFIELD, NEWARRAY, ANEWARRAY, ARRAYLENGTH, ATHROW, CHECKCAST, + * INSTANCEOF, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL + * + * @param insn the bytecode instruction to be interpreted. + * @param value the argument of the instruction to be interpreted. + * @return the result of the interpretation of the given instruction. + * @throws AnalyzerException if an error occured during the interpretation. + */ + Value unaryOperation(AbstractInsnNode insn, Value value) + throws AnalyzerException; + + /** + * Interprets a bytecode instruction with two arguments. This method is + * called for the following opcodes: + * + * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IADD, + * LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, + * LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, ISHL, LSHL, ISHR, LSHR, IUSHR, + * LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, LCMP, FCMPL, FCMPG, DCMPL, + * DCMPG, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, + * IF_ACMPEQ, IF_ACMPNE, PUTFIELD + * + * @param insn the bytecode instruction to be interpreted. + * @param value1 the first argument of the instruction to be interpreted. + * @param value2 the second argument of the instruction to be interpreted. + * @return the result of the interpretation of the given instruction. + * @throws AnalyzerException if an error occured during the interpretation. + */ + Value binaryOperation(AbstractInsnNode insn, Value value1, Value value2) + throws AnalyzerException; + + /** + * Interprets a bytecode instruction with three arguments. This method is + * called for the following opcodes: + * + * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE + * + * @param insn the bytecode instruction to be interpreted. + * @param value1 the first argument of the instruction to be interpreted. + * @param value2 the second argument of the instruction to be interpreted. + * @param value3 the third argument of the instruction to be interpreted. + * @return the result of the interpretation of the given instruction. + * @throws AnalyzerException if an error occured during the interpretation. + */ + Value ternaryOperation( + AbstractInsnNode insn, + Value value1, + Value value2, + Value value3) throws AnalyzerException; + + /** + * Interprets a bytecode instruction with a variable number of arguments. + * This method is called for the following opcodes: + * + * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, + * MULTIANEWARRAY + * + * @param insn the bytecode instruction to be interpreted. + * @param values the arguments of the instruction to be interpreted. + * @return the result of the interpretation of the given instruction. + * @throws AnalyzerException if an error occured during the interpretation. + */ + Value naryOperation(AbstractInsnNode insn, List values) + throws AnalyzerException; + + /** + * Merges two values. The merge operation must return a value that + * represents both values (for instance, if the two values are two types, + * the merged value must be a common super type of the two types. If the two + * values are integer intervals, the merged value must be an interval that + * contains the previous ones. Likewise for other types of values). + * + * @param v a value. + * @param w another value. + * @return the merged value. If the merged value is equal to <tt>v</tt>, + * this method <i>must</i> return <tt>v</tt>. + */ + Value merge(Value v, Value w); +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/SimpleVerifier.java b/asmx/src/org/objectweb/asm/tree/analysis/SimpleVerifier.java new file mode 100644 index 0000000..808b3f4 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/SimpleVerifier.java
@@ -0,0 +1,266 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.List; + +import org.objectweb.asm.Type; + +/** + * An extended {@link BasicVerifier} that performs more precise verifications. + * This verifier computes exact class types, instead of using a single "object + * reference" type (as done in the {@link BasicVerifier}). + * + * @author Eric Bruneton + * @author Bing Ran + */ +public class SimpleVerifier extends BasicVerifier { + + /** + * The class that is verified. + */ + private final Type currentClass; + + /** + * The super class of the class that is verified. + */ + private final Type currentSuperClass; + + /** + * The interfaces implemented by the class that is verified. + */ + private final List currentClassInterfaces; + + /** + * If the class that is verified is an interface. + */ + private final boolean isInterface; + + /** + * Constructs a new {@link SimpleVerifier}. + */ + public SimpleVerifier() { + this(null, null, false); + } + + /** + * Constructs a new {@link SimpleVerifier} to verify a specific class. This + * class will not be loaded into the JVM since it may be incorrect. + * + * @param currentClass the class that is verified. + * @param currentSuperClass the super class of the class that is verified. + * @param isInterface if the class that is verified is an interface. + */ + public SimpleVerifier( + final Type currentClass, + final Type currentSuperClass, + final boolean isInterface) + { + this(currentClass, currentSuperClass, null, isInterface); + } + + /** + * Constructs a new {@link SimpleVerifier} to verify a specific class. This + * class will not be loaded into the JVM since it may be incorrect. + * + * @param currentClass the class that is verified. + * @param currentSuperClass the super class of the class that is verified. + * @param currentClassInterfaces the interfaces implemented by the class + * that is verified. + * @param isInterface if the class that is verified is an interface. + */ + public SimpleVerifier( + final Type currentClass, + final Type currentSuperClass, + final List currentClassInterfaces, + final boolean isInterface) + { + this.currentClass = currentClass; + this.currentSuperClass = currentSuperClass; + this.currentClassInterfaces = currentClassInterfaces; + this.isInterface = isInterface; + } + + public Value newValue(final Type type) { + Value v = super.newValue(type); + if (v == BasicValue.REFERENCE_VALUE) { + v = new BasicValue(type); + } + return v; + } + + protected boolean isArrayValue(final Value value) { + Type t = ((BasicValue) value).getType(); + if (t != null) { + return t.getDescriptor().equals("Lnull;") + || t.getSort() == Type.ARRAY; + } + return false; + } + + protected Value getElementValue(final Value objectArrayValue) + throws AnalyzerException + { + Type arrayType = ((BasicValue) objectArrayValue).getType(); + if (arrayType != null) { + if (arrayType.getSort() == Type.ARRAY) { + return newValue(Type.getType(arrayType.getDescriptor() + .substring(1))); + } else if (arrayType.getDescriptor().equals("Lnull;")) { + return objectArrayValue; + } + } + throw new AnalyzerException("Not an array type"); + } + + protected boolean isSubTypeOf(final Value value, final Value expected) { + Type expectedType = ((BasicValue) expected).getType(); + Type type = ((BasicValue) value).getType(); + if (expectedType == null) { + return type == null; + } + switch (expectedType.getSort()) { + case Type.INT: + case Type.FLOAT: + case Type.LONG: + case Type.DOUBLE: + return type == expectedType; + case Type.ARRAY: + case Type.OBJECT: + if (expectedType.getDescriptor().equals("Lnull;")) { + return type.getSort() == Type.OBJECT + || type.getSort() == Type.ARRAY; + } + if (type.getDescriptor().equals("Lnull;")) { + return true; + } else if (type.getSort() == Type.OBJECT + || type.getSort() == Type.ARRAY) + { + return isAssignableFrom(expectedType, type); + } else { + return false; + } + default: + throw new RuntimeException("Internal error"); + } + } + + public Value merge(final Value v, final Value w) { + if (!v.equals(w)) { + Type t = ((BasicValue) v).getType(); + Type u = ((BasicValue) w).getType(); + if (t != null + && (t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY)) + { + if (u != null + && (u.getSort() == Type.OBJECT || u.getSort() == Type.ARRAY)) + { + if (t.getDescriptor().equals("Lnull;")) { + return w; + } + if (u.getDescriptor().equals("Lnull;")) { + return v; + } + if (isAssignableFrom(t, u)) { + return v; + } + if (isAssignableFrom(u, t)) { + return w; + } + // TODO case of array classes of the same dimension + // TODO should we look also for a common super interface? + // problem: there may be several possible common super + // interfaces + do { + if (t == null || isInterface(t)) { + return BasicValue.REFERENCE_VALUE; + } + t = getSuperClass(t); + if (isAssignableFrom(t, u)) { + return newValue(t); + } + } while (true); + } + } + return BasicValue.UNINITIALIZED_VALUE; + } + return v; + } + + private boolean isInterface(final Type t) { + if (currentClass != null && t.equals(currentClass)) { + return isInterface; + } + return getClass(t).isInterface(); + } + + private Type getSuperClass(final Type t) { + if (currentClass != null && t.equals(currentClass)) { + return currentSuperClass; + } + Class c = getClass(t).getSuperclass(); + return c == null ? null : Type.getType(c); + } + + private boolean isAssignableFrom(final Type t, final Type u) { + if (t.equals(u)) { + return true; + } + if (currentClass != null && t.equals(currentClass)) { + return isAssignableFrom(t, getSuperClass(u)); + } + if (currentClass != null && u.equals(currentClass)) { + if (isAssignableFrom(t, currentSuperClass)) { + return true; + } + if (currentClassInterfaces != null) { + for (int i = 0; i < currentClassInterfaces.size(); ++i) { + Type v = (Type) currentClassInterfaces.get(i); + if (isAssignableFrom(t, v)) { + return true; + } + } + } + return false; + } + return getClass(t).isAssignableFrom(getClass(u)); + } + + protected Class getClass(final Type t) { + try { + if (t.getSort() == Type.ARRAY) { + return Class.forName(t.getDescriptor().replace('/', '.')); + } + return Class.forName(t.getClassName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e.toString()); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/SmallSet.java b/asmx/src/org/objectweb/asm/tree/analysis/SmallSet.java new file mode 100644 index 0000000..74bd590 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/SmallSet.java
@@ -0,0 +1,126 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.AbstractSet; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * A set of at most two elements. + * + * @author Eric Bruneton + */ +class SmallSet extends AbstractSet implements Iterator { + + // if e1 is null, e2 must be null; otherwise e2 must be different from e1 + + Object e1, e2; + + final static SmallSet EMPTY_SET = new SmallSet(null, null); + + SmallSet(Object e1, Object e2) { + this.e1 = e1; + this.e2 = e2; + } + + // ------------------------------------------------------------------------- + // Implementation of inherited abstract methods + // ------------------------------------------------------------------------- + + public Iterator iterator() { + return new SmallSet(e1, e2); + } + + public int size() { + return e1 == null ? 0 : (e2 == null ? 1 : 2); + } + + // ------------------------------------------------------------------------- + // Implementation of the Iterator interface + // ------------------------------------------------------------------------- + + public boolean hasNext() { + return e1 != null; + } + + public Object next() { + Object e = e1; + e1 = e2; + e2 = null; + return e; + } + + public void remove() { + } + + // ------------------------------------------------------------------------- + // Utility methods + // ------------------------------------------------------------------------- + + Set union(SmallSet s) { + if ((s.e1 == e1 && s.e2 == e2) || (s.e1 == e2 && s.e2 == e1)) { + return this; // if the two sets are equal, return this + } + if (s.e1 == null) { + return this; // if s is empty, return this + } + if (e1 == null) { + return s; // if this is empty, return s + } + if (s.e2 == null) { // s contains exactly one element + if (e2 == null) { + return new SmallSet(e1, s.e1); // necessarily e1 != s.e1 + } else if (s.e1 == e1 || s.e1 == e2) { // s is included in this + return this; + } + } + if (e2 == null) { // this contains exactly one element + // if (s.e2 == null) { // cannot happen + // return new SmallSet(e1, s.e1); // necessarily e1 != s.e1 + // } else + if (e1 == s.e1 || e1 == s.e2) { // this in included in s + return s; + } + } + // here we know that there are at least 3 distinct elements + HashSet r = new HashSet(4); + r.add(e1); + if (e2 != null) { + r.add(e2); + } + r.add(s.e1); + if (s.e2 != null) { + r.add(s.e2); + } + return r; + } +}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Subroutine.java b/asmx/src/org/objectweb/asm/tree/analysis/Subroutine.java new file mode 100644 index 0000000..44120a7 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/Subroutine.java
@@ -0,0 +1,96 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.Label; +import org.objectweb.asm.tree.JumpInsnNode; + +/** + * A method subroutine (corresponds to a JSR instruction). + * + * @author Eric Bruneton + */ +class Subroutine { + + Label start; + + boolean[] access; + + List callers; + + private Subroutine() { + } + + public Subroutine( + final Label start, + final int maxLocals, + final JumpInsnNode caller) + { + this.start = start; + this.access = new boolean[maxLocals]; + this.callers = new ArrayList(); + callers.add(caller); + } + + public Subroutine copy() { + Subroutine result = new Subroutine(); + result.start = start; + result.access = new boolean[access.length]; + System.arraycopy(access, 0, result.access, 0, access.length); + result.callers = new ArrayList(callers); + return result; + } + + public boolean merge(final Subroutine subroutine, boolean checkOverlap) + throws AnalyzerException + { + if (checkOverlap && subroutine.start != start) { + throw new AnalyzerException("Overlapping sub routines"); + } + boolean changes = false; + for (int i = 0; i < access.length; ++i) { + if (subroutine.access[i] && !access[i]) { + access[i] = true; + changes = true; + } + } + for (int i = 0; i < subroutine.callers.size(); ++i) { + Object caller = subroutine.callers.get(i); + if (!callers.contains(caller)) { + callers.add(caller); + changes = true; + } + } + return changes; + } +} \ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Value.java b/asmx/src/org/objectweb/asm/tree/analysis/Value.java new file mode 100644 index 0000000..6578b79 --- /dev/null +++ b/asmx/src/org/objectweb/asm/tree/analysis/Value.java
@@ -0,0 +1,45 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.tree.analysis; + +/** + * An immutable symbolic value for semantic interpretation of bytecode. + * + * @author Eric Bruneton + */ +public interface Value { + + /** + * Returns the size of this value in words. + * + * @return either 1 or 2. + */ + int getSize(); +}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierAbstractVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierAbstractVisitor.java new file mode 100644 index 0000000..a621529 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/ASMifierAbstractVisitor.java
@@ -0,0 +1,256 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import java.util.HashMap; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.Type; +import org.objectweb.asm.util.attrs.ASMifiable; + +/** + * An abstract ASMifier visitor. + * + * @author Eric Bruneton + */ +public class ASMifierAbstractVisitor extends AbstractVisitor { + + /** + * The name of the variable for this visitor in the produced code. + */ + protected String name; + + /** + * The label names. This map associates String values to Label keys. It is + * used only in ASMifierMethodVisitor. + */ + HashMap labelNames; + + /** + * Constructs a new {@link ASMifierAbstractVisitor}. + * + * @param name the name of the variable for this visitor in the produced + * code. + */ + protected ASMifierAbstractVisitor(final String name) { + this.name = name; + } + + /** + * Prints the ASM code that generates the given annotation. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values. + */ + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + buf.setLength(0); + buf.append("{\n") + .append("av0 = ") + .append(name) + .append(".visitAnnotation("); + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + /** + * Prints the ASM code that generates the given extended annotation. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values. + */ + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + buf.setLength(0); + buf.append("{\n") + .append("xav0 = ") + .append(name) + .append(".visitTypeAnnotation("); + appendConstant(desc); + buf.append(", ").append(visible); + buf.append(", ").append(inCode).append(");\n"); + text.add(buf.toString()); + ASMifierTypeAnnotationVisitor xav = + new ASMifierTypeAnnotationVisitor(0); + text.add(xav.getText()); + text.add("}\n"); + return xav; + } + + + /** + * Prints the ASM code that generates the given attribute. + * + * @param attr an attribute. + */ + public void visitAttribute(final Attribute attr) { + buf.setLength(0); + if (attr instanceof ASMifiable) { + buf.append("{\n"); + buf.append("// ATTRIBUTE\n"); + ((ASMifiable) attr).asmify(buf, "attr", labelNames); + buf.append(name).append(".visitAttribute(attr);\n"); + buf.append("}\n"); + } else { + buf.append("// WARNING! skipped a non standard attribute of type \""); + buf.append(attr.type).append("\"\n"); + } + text.add(buf.toString()); + } + + /** + * Prints the ASM code to end the visit. + */ + public void visitEnd() { + buf.setLength(0); + buf.append(name).append(".visitEnd();\n"); + text.add(buf.toString()); + } + + /** + * Appends a string representation of the given constant to the given + * buffer. + * + * @param cst an {@link Integer}, {@link Float}, {@link Long}, + * {@link Double} or {@link String} object. May be <tt>null</tt>. + */ + void appendConstant(final Object cst) { + appendConstant(buf, cst); + } + + /** + * Appends a string representation of the given constant to the given + * buffer. + * + * @param buf a string buffer. + * @param cst an {@link Integer}, {@link Float}, {@link Long}, + * {@link Double} or {@link String} object. May be <tt>null</tt>. + */ + static void appendConstant(final StringBuffer buf, final Object cst) { + if (cst == null) { + buf.append("null"); + } else if (cst instanceof String) { + appendString(buf, (String) cst); + } else if (cst instanceof Type) { + buf.append("Type.getType(\""); + buf.append(((Type) cst).getDescriptor()); + buf.append("\")"); + } else if (cst instanceof Byte) { + buf.append("new Byte((byte)").append(cst).append(")"); + } else if (cst instanceof Boolean) { + buf.append("new Boolean(").append(cst).append(")"); + } else if (cst instanceof Short) { + buf.append("new Short((short)").append(cst).append(")"); + } else if (cst instanceof Character) { + int c = ((Character) cst).charValue(); + buf.append("new Character((char)").append(c).append(")"); + } else if (cst instanceof Integer) { + buf.append("new Integer(").append(cst).append(")"); + } else if (cst instanceof Float) { + buf.append("new Float(\"").append(cst).append("\")"); + } else if (cst instanceof Long) { + buf.append("new Long(").append(cst).append("L)"); + } else if (cst instanceof Double) { + buf.append("new Double(\"").append(cst).append("\")"); + } else if (cst instanceof byte[]) { + byte[] v = (byte[]) cst; + buf.append("new byte[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",").append(v[i]); + } + buf.append("}"); + } else if (cst instanceof boolean[]) { + boolean[] v = (boolean[]) cst; + buf.append("new boolean[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",").append(v[i]); + } + buf.append("}"); + } else if (cst instanceof short[]) { + short[] v = (short[]) cst; + buf.append("new short[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",").append("(short)").append(v[i]); + } + buf.append("}"); + } else if (cst instanceof char[]) { + char[] v = (char[]) cst; + buf.append("new char[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",") + .append("(char)") + .append((int) v[i]); + } + buf.append("}"); + } else if (cst instanceof int[]) { + int[] v = (int[]) cst; + buf.append("new int[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",").append(v[i]); + } + buf.append("}"); + } else if (cst instanceof long[]) { + long[] v = (long[]) cst; + buf.append("new long[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",").append(v[i]).append("L"); + } + buf.append("}"); + } else if (cst instanceof float[]) { + float[] v = (float[]) cst; + buf.append("new float[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",").append(v[i]).append("f"); + } + buf.append("}"); + } else if (cst instanceof double[]) { + double[] v = (double[]) cst; + buf.append("new double[] {"); + for (int i = 0; i < v.length; i++) { + buf.append(i == 0 ? "" : ",").append(v[i]).append("d"); + } + buf.append("}"); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierAnnotationVisitor.java new file mode 100644 index 0000000..bb48e2c --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/ASMifierAnnotationVisitor.java
@@ -0,0 +1,127 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; + +/** + * An {@link AnnotationVisitor} that prints the ASM code that generates the + * annotations it visits. + * + * @author Eric Bruneton + */ +public class ASMifierAnnotationVisitor extends AbstractVisitor implements + AnnotationVisitor +{ + + /** + * Identifier of the annotation visitor variable in the produced code. + */ + protected final int id; + + /** + * Constructs a new {@link ASMifierAnnotationVisitor}. + * + * @param id identifier of the annotation visitor variable in the produced + * code. + */ + public ASMifierAnnotationVisitor(final int id) { + this.id = id; + } + + // ------------------------------------------------------------------------ + // Implementation of the AnnotationVisitor interface + // ------------------------------------------------------------------------ + + public void visit(final String name, final Object value) { + buf.setLength(0); + buf.append("av").append(id).append(".visit("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, value); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitEnum( + final String name, + final String desc, + final String value) + { + buf.setLength(0); + buf.append("av").append(id).append(".visitEnum("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, desc); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, value); + buf.append(");\n"); + text.add(buf.toString()); + } + + public AnnotationVisitor visitAnnotation( + final String name, + final String desc) + { + buf.setLength(0); + buf.append("{\n"); + buf.append("AnnotationVisitor av").append(id + 1).append(" = av"); + buf.append(id).append(".visitAnnotation("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, desc); + buf.append(");\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + public AnnotationVisitor visitArray(final String name) { + buf.setLength(0); + buf.append("{\n"); + buf.append("AnnotationVisitor av").append(id + 1).append(" = av"); + buf.append(id).append(".visitArray("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(");\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + public void visitEnd() { + buf.setLength(0); + buf.append("av").append(id).append(".visitEnd();\n"); + text.add(buf.toString()); + } +}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierClassVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierClassVisitor.java new file mode 100644 index 0000000..b89cb65 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/ASMifierClassVisitor.java
@@ -0,0 +1,630 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import java.io.FileInputStream; +import java.io.PrintWriter; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * A {@link ClassVisitor} that prints the ASM code that generates the classes it + * visits. This class visitor can be used to quickly write ASM code to generate + * some given bytecode: <ul> <li>write the Java source code equivalent to the + * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li> + * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the + * {@link #main main} method);</li> <li>edit the generated source code, if + * necessary.</li> </ul> The source code printed when visiting the + * <tt>Hello</tt> class is the following: <p> <blockquote> + * + * <pre> + * import org.objectweb.asm.*; + * + * public class HelloDump implements Opcodes { + * + * public static byte[] dump() throws Exception { + * + * ClassWriter cw = new ClassWriter(false); + * FieldVisitor fv; + * MethodVisitor mv; + * AnnotationVisitor av0; + * + * cw.visit(49, + * ACC_PUBLIC + ACC_SUPER, + * "Hello", + * null, + * "java/lang/Object", + * null); + * + * cw.visitSource("Hello.java", null); + * + * { + * mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + * mv.visitVarInsn(ALOAD, 0); + * mv.visitMethodInsn(INVOKESPECIAL, + * "java/lang/Object", + * "<init>", + * "()V"); + * mv.visitInsn(RETURN); + * mv.visitMaxs(1, 1); + * mv.visitEnd(); + * } + * { + * mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, + * "main", + * "([Ljava/lang/String;)V", + * null, + * null); + * mv.visitFieldInsn(GETSTATIC, + * "java/lang/System", + * "out", + * "Ljava/io/PrintStream;"); + * mv.visitLdcInsn("hello"); + * mv.visitMethodInsn(INVOKEVIRTUAL, + * "java/io/PrintStream", + * "println", + * "(Ljava/lang/String;)V"); + * mv.visitInsn(RETURN); + * mv.visitMaxs(2, 1); + * mv.visitEnd(); + * } + * cw.visitEnd(); + * + * return cw.toByteArray(); + * } + * } + * + * </pre> + * + * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote> + * + * <pre> + * public class Hello { + * + * public static void main(String[] args) { + * System.out.println("hello"); + * } + * } + * </pre> + * + * </blockquote> + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements + ClassVisitor +{ + /** + * Pseudo access flag used to distinguish class access flags. + */ + private final static int ACCESS_CLASS = 262144; + + /** + * Pseudo access flag used to distinguish field access flags. + */ + private final static int ACCESS_FIELD = 524288; + + /** + * Pseudo access flag used to distinguish inner class flags. + */ + private static final int ACCESS_INNER = 1048576; + + /** + * The print writer to be used to print the class. + */ + protected final PrintWriter pw; + + /** + * Prints the ASM source code to generate the given class to the standard + * output. <p> Usage: ASMifierClassVisitor [-debug] <fully qualified + * 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. + */ + public static void main(final String[] args) throws Exception { + int i = 0; + boolean skipDebug = true; + + boolean ok = true; + if (args.length < 1 || args.length > 2) { + ok = false; + } + if (ok && args[0].equals("-debug")) { + i = 1; + skipDebug = false; + if (args.length != 2) { + ok = false; + } + } + if (!ok) { + System.err.println("Prints the ASM code to generate the given class."); + System.err.println("Usage: ASMifierClassVisitor [-debug] " + + "<fully qualified class name or class file name>"); + return; + } + ClassReader cr; + if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1 + || args[i].indexOf('/') > -1) { + cr = new ClassReader(new FileInputStream(args[i])); + } else { + cr = new ClassReader(args[i]); + } + cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), + getDefaultAttributes(), + skipDebug); + } + + /** + * Constructs a new {@link ASMifierClassVisitor} object. + * + * @param pw the print writer to be used to print the class. + */ + public ASMifierClassVisitor(final PrintWriter pw) { + super("cw"); + this.pw = pw; + } + + // ------------------------------------------------------------------------ + // Implementation of the ClassVisitor interface + // ------------------------------------------------------------------------ + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + String simpleName; + int n = name.lastIndexOf('/'); + if (n != -1) { + text.add("package asm." + name.substring(0, n).replace('/', '.') + + ";\n"); + simpleName = name.substring(n + 1); + } else { + simpleName = name; + } + text.add("import java.util.*;\n"); + text.add("import org.objectweb.asm.*;\n"); + text.add("import org.objectweb.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(false);\n"); + text.add("FieldVisitor fv;\n"); + text.add("MethodVisitor mv;\n"); + text.add("AnnotationVisitor av0;\n"); + text.add("TypeAnnotationVisitor xav0;\n\n"); + + buf.setLength(0); + buf.append("cw.visit("); + switch (version) { + case Opcodes.V1_1: + buf.append("V1_1"); + break; + case Opcodes.V1_2: + buf.append("V1_2"); + break; + case Opcodes.V1_3: + buf.append("V1_3"); + break; + case Opcodes.V1_4: + buf.append("V1_4"); + break; + case Opcodes.V1_5: + buf.append("V1_5"); + break; + case Opcodes.V1_6: + buf.append("V1_6"); + break; + default: + buf.append(version); + break; + } + buf.append(", "); + appendAccess(access | ACCESS_CLASS); + buf.append(", "); + appendConstant(name); + buf.append(", "); + appendConstant(signature); + buf.append(", "); + appendConstant(superName); + buf.append(", "); + if (interfaces != null && interfaces.length > 0) { + buf.append("new String[] {"); + for (int i = 0; i < interfaces.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendConstant(interfaces[i]); + } + buf.append(" }"); + } else { + buf.append("null"); + } + buf.append(");\n\n"); + text.add(buf.toString()); + } + + public void visitSource(final String file, final String debug) { + buf.setLength(0); + buf.append("cw.visitSource("); + appendConstant(file); + buf.append(", "); + appendConstant(debug); + buf.append(");\n\n"); + text.add(buf.toString()); + } + + public void visitOuterClass( + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + buf.append("cw.visitOuterClass("); + appendConstant(owner); + buf.append(", "); + appendConstant(name); + buf.append(", "); + appendConstant(desc); + buf.append(");\n\n"); + text.add(buf.toString()); + } + + public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access) + { + buf.setLength(0); + buf.append("cw.visitInnerClass("); + appendConstant(name); + buf.append(", "); + appendConstant(outerName); + buf.append(", "); + appendConstant(innerName); + buf.append(", "); + appendAccess(access | ACCESS_INNER); + buf.append(");\n\n"); + text.add(buf.toString()); + } + + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + buf.setLength(0); + buf.append("{\n"); + buf.append("fv = cw.visitField("); + appendAccess(access | ACCESS_FIELD); + buf.append(", "); + appendConstant(name); + buf.append(", "); + appendConstant(desc); + buf.append(", "); + appendConstant(signature); + buf.append(", "); + appendConstant(value); + buf.append(");\n"); + text.add(buf.toString()); + ASMifierFieldVisitor aav = new ASMifierFieldVisitor(); + text.add(aav.getText()); + text.add("}\n"); + return aav; + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + buf.setLength(0); + buf.append("{\n"); + buf.append("mv = cw.visitMethod("); + appendAccess(access); + buf.append(", "); + appendConstant(name); + buf.append(", "); + appendConstant(desc); + buf.append(", "); + appendConstant(signature); + buf.append(", "); + if (exceptions != null && exceptions.length > 0) { + buf.append("new String[] {"); + for (int i = 0; i < exceptions.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendConstant(exceptions[i]); + } + buf.append(" }"); + } else { + buf.append("null"); + } + buf.append(");\n"); + text.add(buf.toString()); + ASMifierMethodVisitor acv = new ASMifierMethodVisitor(); + text.add(acv.getText()); + text.add("}\n"); + return acv; + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + buf.setLength(0); + buf.append("{\n"); + buf.append("av0 = cw.visitAnnotation("); + appendConstant(desc); + buf.append(", "); + buf.append(visible); + buf.append(");\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + buf.setLength(0); + buf.append("{\n"); + buf.append("xav0 = cw.visitTypeAnnotation("); + appendConstant(desc); + buf.append(", "); + buf.append(visible); + buf.append(", "); + buf.append(inCode); + buf.append(");\n"); + text.add(buf.toString()); + ASMifierTypeAnnotationVisitor xav = + new ASMifierTypeAnnotationVisitor(0); + text.add(xav.getText()); + text.add("}\n"); + return xav; + } + + public void visitEnd() { + text.add("cw.visitEnd();\n\n"); + text.add("return cw.toByteArray();\n"); + text.add("}\n"); + text.add("}\n"); + printList(pw, text); + pw.flush(); + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Appends a string representation of the given access modifiers to {@link + * #buf buf}. + * + * @param access some access modifiers. + */ + void appendAccess(final int access) { + boolean first = true; + if ((access & Opcodes.ACC_PUBLIC) != 0) { + buf.append("ACC_PUBLIC"); + first = false; + } + if ((access & Opcodes.ACC_PRIVATE) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_PRIVATE"); + first = false; + } + if ((access & Opcodes.ACC_PROTECTED) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_PROTECTED"); + first = false; + } + if ((access & Opcodes.ACC_FINAL) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_FINAL"); + first = false; + } + if ((access & Opcodes.ACC_STATIC) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_STATIC"); + first = false; + } + if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { + if (!first) { + buf.append(" + "); + } + if ((access & ACCESS_CLASS) != 0) { + buf.append("ACC_SUPER"); + } else { + buf.append("ACC_SYNCHRONIZED"); + } + first = false; + } + if ((access & Opcodes.ACC_VOLATILE) != 0 + && (access & ACCESS_FIELD) != 0) + { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_VOLATILE"); + first = false; + } + if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0 + && (access & ACCESS_FIELD) == 0) + { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_BRIDGE"); + first = false; + } + if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0 + && (access & ACCESS_FIELD) == 0) + { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_VARARGS"); + first = false; + } + if ((access & Opcodes.ACC_TRANSIENT) != 0 + && (access & ACCESS_FIELD) != 0) + { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_TRANSIENT"); + first = false; + } + if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0 + && (access & ACCESS_FIELD) == 0) + { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_NATIVE"); + first = false; + } + if ((access & Opcodes.ACC_ENUM) != 0 + && ((access & ACCESS_CLASS) != 0 + || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) + { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_ENUM"); + first = false; + } + if ((access & Opcodes.ACC_ANNOTATION) != 0 + && ((access & ACCESS_CLASS) != 0)) + { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_ANNOTATION"); + first = false; + } + if ((access & Opcodes.ACC_ABSTRACT) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_ABSTRACT"); + first = false; + } + if ((access & Opcodes.ACC_INTERFACE) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_INTERFACE"); + first = false; + } + if ((access & Opcodes.ACC_STRICT) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_STRICT"); + first = false; + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_SYNTHETIC"); + first = false; + } + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + if (!first) { + buf.append(" + "); + } + buf.append("ACC_DEPRECATED"); + first = false; + } + if (first) { + buf.append("0"); + } + } + + /** + * Appends a string representation of the given constant to the given + * buffer. + * + * @param buf a string buffer. + * @param cst an {@link java.lang.Integer Integer}, {@link java.lang.Float + * Float}, {@link java.lang.Long Long}, + * {@link java.lang.Double Double} or {@link String String} object. + * May be <tt>null</tt>. + */ + static void appendConstant(final StringBuffer buf, final Object cst) { + if (cst == null) { + buf.append("null"); + } else if (cst instanceof String) { + AbstractVisitor.appendString(buf, (String) cst); + } else if (cst instanceof Type) { + buf.append("Type.getType(\"") + .append(((Type) cst).getDescriptor()) + .append("\")"); + } else if (cst instanceof Integer) { + buf.append("new Integer(").append(cst).append(")"); + } else if (cst instanceof Float) { + buf.append("new Float(\"").append(cst).append("\")"); + } else if (cst instanceof Long) { + buf.append("new Long(").append(cst).append("L)"); + } else if (cst instanceof Double) { + buf.append("new Double(\"").append(cst).append("\")"); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierFieldVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierFieldVisitor.java new file mode 100644 index 0000000..d9f7804 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/ASMifierFieldVisitor.java
@@ -0,0 +1,49 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.FieldVisitor; + +/** + * A {@link FieldVisitor} that prints the ASM code that generates the fields it + * visits. + * + * @author Eric Bruneton + */ +public class ASMifierFieldVisitor extends ASMifierAbstractVisitor implements + FieldVisitor +{ + /** + * Constructs a new {@link ASMifierFieldVisitor}. + */ + public ASMifierFieldVisitor() { + super("fv"); + } +}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierMethodVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierMethodVisitor.java new file mode 100644 index 0000000..4459860 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/ASMifierMethodVisitor.java
@@ -0,0 +1,382 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import java.util.HashMap; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.TypePath; + +/** + * A {@link MethodVisitor} that prints the ASM code that generates the methods + * it visits. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class ASMifierMethodVisitor extends ASMifierAbstractVisitor implements + MethodVisitor +{ + /** + * Constructs a new {@link ASMifierMethodVisitor} object. + */ + public ASMifierMethodVisitor() { + super("mv"); + this.labelNames = new HashMap(); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + buf.setLength(0); + buf.append("{\n").append("av0 = mv.visitAnnotationDefault();\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + @Override + public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible) + { + buf.setLength(0); + buf.append("{\n") + .append("av0 = mv.visitParameterAnnotation(") + .append(parameter) + .append(", "); + appendConstant(desc); + buf.append(", ").append(visible).append(");\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + @Override + public void visitCode() { + text.add("mv.visitCode();\n"); + } + + @Override + public void visitInsn(final int opcode) { + buf.setLength(0); + buf.append("mv.visitInsn(").append(OPCODES[opcode]).append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitIntInsn(final int opcode, final int operand) { + buf.setLength(0); + buf.append("mv.visitIntInsn(") + .append(OPCODES[opcode]) + .append(", ") + .append(opcode == Opcodes.NEWARRAY + ? TYPES[operand] + : Integer.toString(operand)) + .append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitVarInsn(final int opcode, final int var) { + buf.setLength(0); + buf.append("mv.visitVarInsn(") + .append(OPCODES[opcode]) + .append(", ") + .append(var) + .append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitTypeInsn(final int opcode, final String desc) { + buf.setLength(0); + buf.append("mv.visitTypeInsn(").append(OPCODES[opcode]).append(", "); + appendConstant(desc); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + buf.append("mv.visitFieldInsn(").append(OPCODES[opcode]).append(", "); + appendConstant(owner); + buf.append(", "); + appendConstant(name); + buf.append(", "); + appendConstant(desc); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + buf.append("mv.visitMethodInsn(").append(OPCODES[opcode]).append(", "); + appendConstant(owner); + buf.append(", "); + appendConstant(name); + buf.append(", "); + appendConstant(desc); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + // TODO Auto-generated method stub + + } + + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void visitJumpInsn(final int opcode, final Label label) { + buf.setLength(0); + declareLabel(label); + buf.append("mv.visitJumpInsn(").append(OPCODES[opcode]).append(", "); + appendLabel(label); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitLabel(final Label label) { + buf.setLength(0); + declareLabel(label); + buf.append("mv.visitLabel("); + appendLabel(label); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitLdcInsn(final Object cst) { + buf.setLength(0); + buf.append("mv.visitLdcInsn("); + appendConstant(cst); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitIincInsn(final int var, final int increment) { + buf.setLength(0); + buf.append("mv.visitIincInsn(") + .append(var) + .append(", ") + .append(increment) + .append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + buf.setLength(0); + for (int i = 0; i < labels.length; ++i) { + declareLabel(labels[i]); + } + declareLabel(dflt); + + buf.append("mv.visitTableSwitchInsn(") + .append(min) + .append(", ") + .append(max) + .append(", "); + appendLabel(dflt); + buf.append(", new Label[] {"); + for (int i = 0; i < labels.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(labels[i]); + } + buf.append(" });\n"); + text.add(buf.toString()); + } + + @Override + public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]) + { + buf.setLength(0); + for (int i = 0; i < labels.length; ++i) { + declareLabel(labels[i]); + } + declareLabel(dflt); + + buf.append("mv.visitLookupSwitchInsn("); + appendLabel(dflt); + buf.append(", new int[] {"); + for (int i = 0; i < keys.length; ++i) { + buf.append(i == 0 ? " " : ", ").append(keys[i]); + } + buf.append(" }, new Label[] {"); + for (int i = 0; i < labels.length; ++i) { + buf.append(i == 0 ? " " : ", "); + appendLabel(labels[i]); + } + buf.append(" });\n"); + text.add(buf.toString()); + } + + @Override + public void visitMultiANewArrayInsn(final String desc, final int dims) { + buf.setLength(0); + buf.append("mv.visitMultiANewArrayInsn("); + appendConstant(desc); + buf.append(", ").append(dims).append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type) + { + buf.setLength(0); + declareLabel(start); + declareLabel(end); + declareLabel(handler); + buf.append("mv.visitTryCatchBlock("); + appendLabel(start); + buf.append(", "); + appendLabel(end); + buf.append(", "); + appendLabel(handler); + buf.append(", "); + appendConstant(type); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + buf.setLength(0); + buf.append("mv.visitLocalVariable("); + appendConstant(name); + buf.append(", "); + appendConstant(desc); + buf.append(", "); + appendConstant(signature); + buf.append(", "); + appendLabel(start); + buf.append(", "); + appendLabel(end); + buf.append(", ").append(index).append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitLineNumber(final int line, final Label start) { + buf.setLength(0); + buf.append("mv.visitLineNumber(").append(line).append(", "); + appendLabel(start); + buf.append(");\n"); + text.add(buf.toString()); + } + + @Override + public void visitMaxs(final int maxStack, final int maxLocals) { + buf.setLength(0); + buf.append("mv.visitMaxs(") + .append(maxStack) + .append(", ") + .append(maxLocals) + .append(");\n"); + text.add(buf.toString()); + } + + /** + * Appends a declaration of the given label to {@link #buf buf}. This + * declaration is of the form "Label lXXX = new Label();". Does nothing if + * the given label has already been declared. + * + * @param l a label. + */ + private void declareLabel(final Label l) { + String name = (String) labelNames.get(l); + if (name == null) { + name = "l" + labelNames.size(); + labelNames.put(l, name); + buf.append("Label ").append(name).append(" = new Label();\n"); + } + } + + /** + * Appends the name of the given label to {@link #buf buf}. The given label + * <i>must</i> already have a name. One way to ensure this is to always + * call {@link #declareLabel declared} before calling this method. + * + * @param l a label. + */ + private void appendLabel(final Label l) { + buf.append((String) labelNames.get(l)); + } +}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierTypeAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierTypeAnnotationVisitor.java new file mode 100644 index 0000000..499f82f --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/ASMifierTypeAnnotationVisitor.java
@@ -0,0 +1,205 @@ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.TypeAnnotationVisitor; + +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +/** + * An {@link TypeAnnotationVisitor} that prints the ASM code that generates + * the extended annotations it visits. + * + * @author jaimeq + */ +public class ASMifierTypeAnnotationVisitor extends AbstractVisitor + implements TypeAnnotationVisitor +{ + + /** + * Identifier of the extended annotation visitor variable in the + * produced code. + */ + protected final int id; + + /** + * Constructs a new {@link ASMifierTypeAnnotationVisitor}. + * + * @param id identifier of the extended annotation visitor variable in the + * produced code. + */ + public ASMifierTypeAnnotationVisitor(final int id) { + this.id = id; + } + + // ------------------------------------------------------------------------ + // Implementation of the TypeAnnotationVisitor interface + // ------------------------------------------------------------------------ + + public void visit(final String name, final Object value) { + buf.setLength(0); + buf.append("xav").append(id).append(".visit("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, value); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitEnum( + final String name, + final String desc, + final String value) + { + buf.setLength(0); + buf.append("xav").append(id).append(".visitEnum("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, desc); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, value); + buf.append(");\n"); + text.add(buf.toString()); + } + + public AnnotationVisitor visitAnnotation( + final String name, + final String desc) + { + buf.setLength(0); + buf.append("{\n"); + buf.append("AnnotationVisitor av").append(id + 1).append(" = av"); + buf.append(id).append(".visitAnnotation("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(", "); + ASMifierAbstractVisitor.appendConstant(buf, desc); + buf.append(");\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + public AnnotationVisitor visitArray(final String name) { + buf.setLength(0); + buf.append("{\n"); + buf.append("AnnotationVisitor av").append(id + 1).append(" = av"); + buf.append(id).append(".visitArray("); + ASMifierAbstractVisitor.appendConstant(buf, name); + buf.append(");\n"); + text.add(buf.toString()); + ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1); + text.add(av.getText()); + text.add("}\n"); + return av; + } + + public void visitEnd() { + buf.setLength(0); + buf.append("xav").append(id).append(".visitEnd();\n"); + text.add(buf.toString()); + } + + public void appendConstant(final StringBuffer buf, int i) { + ASMifierAbstractVisitor.appendConstant(buf, Integer.valueOf(i)); + } + + public void visitXTargetType(int target_type) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXTargetType("); + appendConstant(buf, target_type); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXOffset(int offset) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXOffset("); + appendConstant(buf, offset); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXLocationLength(int location_length) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXLocationLength("); + appendConstant(buf, location_length); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXLocation(TypePathEntry location) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXLocation("); + buf.append(location.toString()); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXNumEntries(int num_entries) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXNumEntries("); + appendConstant(buf, num_entries); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXStartPc(int start_pc) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXStartPc("); + appendConstant(buf, start_pc); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXLength(int length) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXLength("); + appendConstant(buf, length); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXIndex(int index) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXIndex("); + appendConstant(buf, index); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXParamIndex(int param_index) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXParamIndex("); + appendConstant(buf, param_index); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXBoundIndex(int bound_index) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXBoundIndex("); + appendConstant(buf, bound_index); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXTypeIndex(int type_index) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXTypeIndex("); + appendConstant(buf, type_index); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXExceptionIndex(int exception_index) { + buf.setLength(0); + buf.append("xav").append(id).append(".visitXExceptionIndex("); + appendConstant(buf, exception_index); + buf.append(");\n"); + text.add(buf.toString()); + } + + public void visitXNameAndArgsSize() { + } +}
diff --git a/asmx/src/org/objectweb/asm/util/AbstractVisitor.java b/asmx/src/org/objectweb/asm/util/AbstractVisitor.java new file mode 100644 index 0000000..3e329f7 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/AbstractVisitor.java
@@ -0,0 +1,201 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.util.attrs.ASMStackMapAttribute; +import org.objectweb.asm.util.attrs.ASMStackMapTableAttribute; + +/** + * An abstract visitor. + * + * @author Eric Bruneton + */ +public abstract class AbstractVisitor { + + /** + * The names of the Java Virtual Machine opcodes. + */ + public final static String[] OPCODES; + /** + * Types for <code>operand</code> parameter of the + * {@link org.objectweb.asm.MethodVisitor#visitIntInsn} method when + * <code>opcode</code> is <code>NEWARRAY</code>. + */ + public final static String[] TYPES; + + static { + String s = "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,BIPUSH,SIPUSH,LDC,,," + + "ILOAD,LLOAD,FLOAD,DLOAD,ALOAD,,,,,,,,,,,,,,,,,,,,,IALOAD," + + "LALOAD,FALOAD,DALOAD,AALOAD,BALOAD,CALOAD,SALOAD,ISTORE," + + "LSTORE,FSTORE,DSTORE,ASTORE,,,,,,,,,,,,,,,,,,,,,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,IINC,I2L," + + "I2F,I2D,L2I,L2F,L2D,F2I,F2L,F2D,D2I,D2L,D2F,I2B,I2C,I2S,LCMP," + + "FCMPL,FCMPG,DCMPL,DCMPG,IFEQ,IFNE,IFLT,IFGE,IFGT,IFLE," + + "IF_ICMPEQ,IF_ICMPNE,IF_ICMPLT,IF_ICMPGE,IF_ICMPGT,IF_ICMPLE," + + "IF_ACMPEQ,IF_ACMPNE,GOTO,JSR,RET,TABLESWITCH,LOOKUPSWITCH," + + "IRETURN,LRETURN,FRETURN,DRETURN,ARETURN,RETURN,GETSTATIC," + + "PUTSTATIC,GETFIELD,PUTFIELD,INVOKEVIRTUAL,INVOKESPECIAL," + + "INVOKESTATIC,INVOKEINTERFACE,,NEW,NEWARRAY,ANEWARRAY," + + "ARRAYLENGTH,ATHROW,CHECKCAST,INSTANCEOF,MONITORENTER," + + "MONITOREXIT,,MULTIANEWARRAY,IFNULL,IFNONNULL,"; + OPCODES = new String[200]; + int i = 0; + int j = 0; + int l; + while ((l = s.indexOf(',', j)) > 0) { + OPCODES[i++] = j + 1 == l ? null : s.substring(j, l); + j = l + 1; + } + + s = "T_BOOLEAN,T_CHAR,T_FLOAT,T_DOUBLE,T_BYTE,T_SHORT,T_INT,T_LONG,"; + TYPES = new String[12]; + j = 0; + i = 4; + while ((l = s.indexOf(',', j)) > 0) { + TYPES[i++] = s.substring(j, l); + j = l + 1; + } + } + + /** + * The text to be printed. Since the code of methods is not necessarily + * visited in sequential order, one method after the other, but can be + * interlaced (some instructions from method one, then some instructions + * from method two, then some instructions from method one again...), it is + * not possible to print the visited instructions directly to a sequential + * stream. A class is therefore printed in a two steps process: a string + * tree is constructed during the visit, and printed to a sequential stream + * at the end of the visit. This string tree is stored in this field, as a + * string list that can contain other string lists, which can themselves + * contain other string lists, and so on. + */ + public final List text; + + /** + * A buffer that can be used to create strings. + */ + protected final StringBuffer buf; + + /** + * Constructs a new {@link AbstractVisitor}. + */ + protected AbstractVisitor() { + this.text = new ArrayList(); + this.buf = new StringBuffer(); + } + + /** + * Returns the text printed by this visitor. + * + * @return the text printed by this visitor. + */ + public List getText() { + return text; + } + + /** + * Appends a quoted string to a given buffer. + * + * @param buf the buffer where the string must be added. + * @param s the string to be added. + */ + public static void appendString(final StringBuffer buf, final String s) { + buf.append("\""); + for (int i = 0; i < s.length(); ++i) { + char c = s.charAt(i); + if (c == '\n') { + buf.append("\\n"); + } else if (c == '\r') { + buf.append("\\r"); + } else if (c == '\\') { + buf.append("\\\\"); + } else if (c == '"') { + buf.append("\\\""); + } else if (c < 0x20 || c > 0x7f) { + buf.append("\\u"); + if (c < 0x10) { + buf.append("000"); + } else if (c < 0x100) { + buf.append("00"); + } else if (c < 0x1000) { + buf.append("0"); + } + buf.append(Integer.toString(c, 16)); + } else { + buf.append(c); + } + } + buf.append("\""); + } + + /** + * Prints the given string tree. + * + * @param pw the writer to be used to print the tree. + * @param l a string tree, i.e., a string list that can contain other string + * lists, and so on recursively. + */ + void printList(final PrintWriter pw, final List l) { + for (int i = 0; i < l.size(); ++i) { + Object o = l.get(i); + if (o instanceof List) { + printList(pw, (List) o); + } else { + pw.print(o.toString()); + } + } + } + + /** + * Returns the default {@link ASMifiable} prototypes. + * + * @return the default {@link ASMifiable} prototypes. + */ + public static Attribute[] getDefaultAttributes() { + try { + return new Attribute[] { + new ASMStackMapAttribute(), + new ASMStackMapTableAttribute() }; + } catch (Exception e) { + return new Attribute[0]; + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/CheckAnnotationAdapter.java b/asmx/src/org/objectweb/asm/util/CheckAnnotationAdapter.java new file mode 100644 index 0000000..d00933c --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/CheckAnnotationAdapter.java
@@ -0,0 +1,125 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Type; + +/** + * An {@link AnnotationVisitor} that checks that its methods are properly used. + * + * @author Eric Bruneton + */ +public class CheckAnnotationAdapter implements AnnotationVisitor { + + private AnnotationVisitor av; + + private boolean named; + + private boolean end; + + public CheckAnnotationAdapter(final AnnotationVisitor av) { + this(av, true); + } + + CheckAnnotationAdapter( + final AnnotationVisitor av, + final boolean named) + { + this.av = av; + this.named = named; + } + + public void visit(final String name, final Object value) { + checkEnd(); + checkName(name); + if (!(value instanceof Byte || value instanceof Boolean + || value instanceof Character || value instanceof Short + || value instanceof Integer || value instanceof Long + || value instanceof Float || value instanceof Double + || value instanceof String || value instanceof Type + || value instanceof byte[] || value instanceof boolean[] + || value instanceof char[] || value instanceof short[] + || value instanceof int[] || value instanceof long[] + || value instanceof float[] || value instanceof double[])) + { + throw new IllegalArgumentException("Invalid annotation value"); + } + av.visit(name, value); + } + + public void visitEnum( + final String name, + final String desc, + final String value) + { + checkEnd(); + checkName(name); + CheckMethodAdapter.checkDesc(desc, false); + if (value == null) { + throw new IllegalArgumentException("Invalid enum value"); + } + av.visitEnum(name, desc, value); + } + + public AnnotationVisitor visitAnnotation( + final String name, + final String desc) + { + checkEnd(); + checkName(name); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(av.visitAnnotation(name, desc)); + } + + public AnnotationVisitor visitArray(final String name) { + checkEnd(); + checkName(name); + return new CheckAnnotationAdapter(av.visitArray(name), false); + } + + public void visitEnd() { + checkEnd(); + end = true; + av.visitEnd(); + } + + private void checkEnd() { + if (end) { + throw new IllegalStateException("Cannot call a visit method after visitEnd has been called"); + } + } + + private void checkName(final String name) { + if (named && name == null) { + throw new IllegalArgumentException("Annotation value name must not be null"); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/CheckClassAdapter.java b/asmx/src/org/objectweb/asm/util/CheckClassAdapter.java new file mode 100644 index 0000000..640e8b0 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/CheckClassAdapter.java
@@ -0,0 +1,416 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import java.io.FileInputStream; +import java.io.PrintWriter; +import java.util.List; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.ClassAdapter; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.TryCatchBlockNode; +import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.SimpleVerifier; +import org.objectweb.asm.tree.analysis.Frame; + +/** + * A {@link ClassAdapter} that checks that its methods are properly used. More + * precisely this class adapter checks each method call individually, based + * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i> + * of method calls. For example, the invalid sequence + * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC, + * "i", "D", null)</tt> + * will <i>not</i> be detected by this class adapter. + * + * @author Eric Bruneton + */ +public class CheckClassAdapter extends ClassAdapter { + + /** + * <tt>true</tt> if the visit method has been called. + */ + private boolean start; + + /** + * <tt>true</tt> if the visitSource method has been called. + */ + private boolean source; + + /** + * <tt>true</tt> if the visitOuterClass method has been called. + */ + private boolean outer; + + /** + * <tt>true</tt> if the visitEnd method has been called. + */ + private boolean end; + + /** + * Checks a given class. <p> Usage: CheckClassAdapter <fully qualified + * 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. + */ + public static void main(final String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Verifies the given class."); + System.err.println("Usage: CheckClassAdapter " + + "<fully qualified class name or class file name>"); + return; + } + ClassReader cr; + if (args[0].endsWith(".class")) { + cr = new ClassReader(new FileInputStream(args[0])); + } else { + cr = new ClassReader(args[0]); + } + + verify(cr, false, new PrintWriter(System.err)); + } + + /** + * Checks a given class + * + * @param cr a <code>ClassReader</code> that contains bytecode for the analysis. + * @param dump true if bytecode should be printed out not only when errors are found. + * @param pw write where results going to be printed + */ + public static void verify(ClassReader cr, boolean dump, PrintWriter pw) { + ClassNode cn = new ClassNode(); + cr.accept(new CheckClassAdapter(cn), true); + + 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 SimpleVerifier(Type.getType("L" + + cn.name + ";"), + Type.getType("L" + cn.superName + ";"), + (cn.access & Opcodes.ACC_INTERFACE) != 0)); + try { + a.analyze(cn.name, method); + if (!dump) { + continue; + } + } catch (Exception e) { + e.printStackTrace(); + } + Frame[] frames = a.getFrames(); + + TraceMethodVisitor mv = new TraceMethodVisitor(); + + pw.println(method.name + method.desc); + for (int j = 0; j < method.instructions.size(); ++j) { + ((AbstractInsnNode) method.instructions.get(j)).accept(mv); + + StringBuffer s = new StringBuffer(); + Frame f = frames[j]; + if (f == null) { + s.append('?'); + } else { + for (int k = 0; k < f.getLocals(); ++k) { + s.append(getShortName(f.getLocal(k).toString())) + .append(' '); + } + s.append(" : "); + for (int k = 0; k < f.getStackSize(); ++k) { + s.append(getShortName(f.getStack(k).toString())) + .append(' '); + } + } + while (s.length() < method.maxStack + method.maxLocals + 1) + { + s.append(' '); + } + pw.print(Integer.toString(j + 100000).substring(1)); + pw.print(" " + s + " : " + mv.buf); // mv.text.get(j)); + } + for (int j = 0; j < method.tryCatchBlocks.size(); ++j) { + ((TryCatchBlockNode) method.tryCatchBlocks.get(j)).accept(mv); + pw.print(" " + mv.buf); + } + pw.println(); + } + } + } + + private static String getShortName(String name) { + int n = name.lastIndexOf('/'); + int k = name.length(); + if(name.charAt(k-1)==';') k--; + return n==-1 ? name : name.substring(n+1, k); + } + + /** + * Constructs a new {@link CheckClassAdapter}. + * + * @param cv the class visitor to which this adapter must delegate calls. + */ + public CheckClassAdapter(final ClassVisitor cv) { + super(cv); + } + + // ------------------------------------------------------------------------ + // Implementation of the ClassVisitor interface + // ------------------------------------------------------------------------ + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + if (start) { + throw new IllegalStateException("visit must be called only once"); + } else { + start = true; + } + checkState(); + checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE + + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC + + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM + + Opcodes.ACC_DEPRECATED); + CheckMethodAdapter.checkInternalName(name, "class name"); + if ("java/lang/Object".equals(name)) { + if (superName != null) { + throw new IllegalArgumentException("The super class name of the Object class must be 'null'"); + } + } else { + CheckMethodAdapter.checkInternalName(superName, "super class name"); + } + if (signature != null) { + // TODO + } + if ((access & Opcodes.ACC_INTERFACE) != 0) { + if (!"java/lang/Object".equals(superName)) { + throw new IllegalArgumentException("The super class name of interfaces must be 'java/lang/Object'"); + } + } + if (interfaces != null) { + for (int i = 0; i < interfaces.length; ++i) { + CheckMethodAdapter.checkInternalName(interfaces[i], + "interface name at index " + i); + } + } + cv.visit(version, access, name, signature, superName, interfaces); + } + + public void visitSource(final String file, final String debug) { + checkState(); + if (source) { + throw new IllegalStateException("visitSource can be called only once."); + } + source = true; + cv.visitSource(file, debug); + } + + public void visitOuterClass( + final String owner, + final String name, + final String desc) + { + checkState(); + if (outer) { + throw new IllegalStateException("visitSource can be called only once."); + } + outer = true; + if (owner == null) { + throw new IllegalArgumentException("Illegal outer class owner"); + } + if (desc != null) { + CheckMethodAdapter.checkMethodDesc(desc); + } + cv.visitOuterClass(owner, name, desc); + } + + public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access) + { + checkState(); + CheckMethodAdapter.checkInternalName(name, "class name"); + if (outerName != null) { + CheckMethodAdapter.checkInternalName(outerName, "outer class name"); + } + if (innerName != null) { + CheckMethodAdapter.checkIdentifier(innerName, "inner class name"); + } + checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE + + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC + + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE + + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC + + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM); + cv.visitInnerClass(name, outerName, innerName, access); + } + + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + checkState(); + checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE + + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC + + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE + + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC + + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED); + CheckMethodAdapter.checkIdentifier(name, "field name"); + CheckMethodAdapter.checkDesc(desc, false); + if (signature != null) { + // TODO + } + if (value != null) { + CheckMethodAdapter.checkConstant(value); + } + FieldVisitor av = cv.visitField(access, name, desc, signature, value); + return new CheckFieldAdapter(av); + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + checkState(); + checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE + + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC + + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED + + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE + + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT + + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED); + CheckMethodAdapter.checkMethodIdentifier(name, "method name"); + CheckMethodAdapter.checkMethodDesc(desc); + if (signature != null) { + // TODO + } + if (exceptions != null) { + for (int i = 0; i < exceptions.length; ++i) { + CheckMethodAdapter.checkInternalName(exceptions[i], + "exception name at index " + i); + } + } + return new CheckMethodAdapter(cv.visitMethod(access, + name, + desc, + signature, + exceptions)); + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + checkState(); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(cv.visitAnnotation(desc, visible)); + } + + public void visitAttribute(final Attribute attr) { + checkState(); + if (attr == null) { + throw new IllegalArgumentException("Invalid attribute (must not be null)"); + } + cv.visitAttribute(attr); + } + + public void visitEnd() { + checkState(); + end = true; + cv.visitEnd(); + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * Checks that the visit method has been called and that visitEnd has not + * been called. + */ + private void checkState() { + if (!start) { + throw new IllegalStateException("Cannot visit member before visit has been called."); + } + if (end) { + throw new IllegalStateException("Cannot visit member after visitEnd has been called."); + } + } + + /** + * Checks that the given access flags do not contain invalid flags. This + * method also checks that mutually incompatible flags are not set + * simultaneously. + * + * @param access the access flags to be checked + * @param possibleAccess the valid access flags. + */ + static void checkAccess(final int access, final int possibleAccess) { + if ((access & ~possibleAccess) != 0) { + throw new IllegalArgumentException("Invalid access flags: " + + access); + } + int pub = ((access & Opcodes.ACC_PUBLIC) != 0 ? 1 : 0); + int pri = ((access & Opcodes.ACC_PRIVATE) != 0 ? 1 : 0); + int pro = ((access & Opcodes.ACC_PROTECTED) != 0 ? 1 : 0); + if (pub + pri + pro > 1) { + throw new IllegalArgumentException("public private and protected are mutually exclusive: " + + access); + } + int fin = ((access & Opcodes.ACC_FINAL) != 0 ? 1 : 0); + int abs = ((access & Opcodes.ACC_ABSTRACT) != 0 ? 1 : 0); + if (fin + abs > 1) { + throw new IllegalArgumentException("final and abstract are mutually exclusive: " + + access); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/CheckFieldAdapter.java b/asmx/src/org/objectweb/asm/util/CheckFieldAdapter.java new file mode 100644 index 0000000..e267093 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/CheckFieldAdapter.java
@@ -0,0 +1,85 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; + +/** + * A {@link FieldVisitor} that checks that its methods are properly used. + */ +public class CheckFieldAdapter implements FieldVisitor { + + private FieldVisitor fv; + + private boolean end; + + public CheckFieldAdapter(final FieldVisitor fv) { + this.fv = fv; + } + + public AnnotationVisitor visitAnnotation(final String desc, boolean visible) + { + checkEnd(); + CheckMethodAdapter.checkDesc(desc, false); + return new CheckAnnotationAdapter(fv.visitAnnotation(desc, visible)); + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + throw new RuntimeException("Jaime did not implement yet"); + } + //end jaime + + public void visitAttribute(final Attribute attr) { + checkEnd(); + if (attr == null) { + throw new IllegalArgumentException("Invalid attribute (must not be null)"); + } + fv.visitAttribute(attr); + } + + public void visitEnd() { + checkEnd(); + end = true; + fv.visitEnd(); + } + + private void checkEnd() { + if (end) { + throw new IllegalStateException("Cannot call a visit method after visitEnd has been called"); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/CheckMethodAdapter.java b/asmx/src/org/objectweb/asm/util/CheckMethodAdapter.java new file mode 100644 index 0000000..7844077 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/CheckMethodAdapter.java
@@ -0,0 +1,942 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodAdapter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.Type; + +import java.util.HashMap; + +/** + * A {@link MethodAdapter} that checks that its methods are properly used. More + * precisely this code adapter checks each instruction individually (i.e., each + * visit method checks some preconditions based <i>only</i> on its arguments - + * such as the fact that the given opcode is correct for a given visit method), + * but does <i>not</i> check the <i>sequence</i> of instructions. For example, + * in a method whose signature is <tt>void m ()</tt>, the invalid instruction + * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by + * this code adapter. + * + * @author Eric Bruneton + */ +public class CheckMethodAdapter extends MethodAdapter { + + /** + * <tt>true</tt> if the visitCode method has been called. + */ + private boolean startCode; + + /** + * <tt>true</tt> if the visitMaxs method has been called. + */ + private boolean endCode; + + /** + * <tt>true</tt> if the visitEnd method has been called. + */ + private boolean endMethod; + + /** + * The already visited labels. This map associate Integer values to Label + * keys. + */ + private HashMap labels; + + /** + * Code of the visit method to be used for each opcode. + */ + private final static int[] TYPE; + + static { + String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD" + + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" + + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD" + + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA"; + TYPE = new int[s.length()]; + for (int i = 0; i < TYPE.length; ++i) { + TYPE[i] = (s.charAt(i) - 'A' - 1); + } + } + + // code to generate the above string + // public static void main (String[] args) { + // int[] TYPE = new int[] { + // 0, //NOP + // 0, //ACONST_NULL + // 0, //ICONST_M1 + // 0, //ICONST_0 + // 0, //ICONST_1 + // 0, //ICONST_2 + // 0, //ICONST_3 + // 0, //ICONST_4 + // 0, //ICONST_5 + // 0, //LCONST_0 + // 0, //LCONST_1 + // 0, //FCONST_0 + // 0, //FCONST_1 + // 0, //FCONST_2 + // 0, //DCONST_0 + // 0, //DCONST_1 + // 1, //BIPUSH + // 1, //SIPUSH + // 7, //LDC + // -1, //LDC_W + // -1, //LDC2_W + // 2, //ILOAD + // 2, //LLOAD + // 2, //FLOAD + // 2, //DLOAD + // 2, //ALOAD + // -1, //ILOAD_0 + // -1, //ILOAD_1 + // -1, //ILOAD_2 + // -1, //ILOAD_3 + // -1, //LLOAD_0 + // -1, //LLOAD_1 + // -1, //LLOAD_2 + // -1, //LLOAD_3 + // -1, //FLOAD_0 + // -1, //FLOAD_1 + // -1, //FLOAD_2 + // -1, //FLOAD_3 + // -1, //DLOAD_0 + // -1, //DLOAD_1 + // -1, //DLOAD_2 + // -1, //DLOAD_3 + // -1, //ALOAD_0 + // -1, //ALOAD_1 + // -1, //ALOAD_2 + // -1, //ALOAD_3 + // 0, //IALOAD + // 0, //LALOAD + // 0, //FALOAD + // 0, //DALOAD + // 0, //AALOAD + // 0, //BALOAD + // 0, //CALOAD + // 0, //SALOAD + // 2, //ISTORE + // 2, //LSTORE + // 2, //FSTORE + // 2, //DSTORE + // 2, //ASTORE + // -1, //ISTORE_0 + // -1, //ISTORE_1 + // -1, //ISTORE_2 + // -1, //ISTORE_3 + // -1, //LSTORE_0 + // -1, //LSTORE_1 + // -1, //LSTORE_2 + // -1, //LSTORE_3 + // -1, //FSTORE_0 + // -1, //FSTORE_1 + // -1, //FSTORE_2 + // -1, //FSTORE_3 + // -1, //DSTORE_0 + // -1, //DSTORE_1 + // -1, //DSTORE_2 + // -1, //DSTORE_3 + // -1, //ASTORE_0 + // -1, //ASTORE_1 + // -1, //ASTORE_2 + // -1, //ASTORE_3 + // 0, //IASTORE + // 0, //LASTORE + // 0, //FASTORE + // 0, //DASTORE + // 0, //AASTORE + // 0, //BASTORE + // 0, //CASTORE + // 0, //SASTORE + // 0, //POP + // 0, //POP2 + // 0, //DUP + // 0, //DUP_X1 + // 0, //DUP_X2 + // 0, //DUP2 + // 0, //DUP2_X1 + // 0, //DUP2_X2 + // 0, //SWAP + // 0, //IADD + // 0, //LADD + // 0, //FADD + // 0, //DADD + // 0, //ISUB + // 0, //LSUB + // 0, //FSUB + // 0, //DSUB + // 0, //IMUL + // 0, //LMUL + // 0, //FMUL + // 0, //DMUL + // 0, //IDIV + // 0, //LDIV + // 0, //FDIV + // 0, //DDIV + // 0, //IREM + // 0, //LREM + // 0, //FREM + // 0, //DREM + // 0, //INEG + // 0, //LNEG + // 0, //FNEG + // 0, //DNEG + // 0, //ISHL + // 0, //LSHL + // 0, //ISHR + // 0, //LSHR + // 0, //IUSHR + // 0, //LUSHR + // 0, //IAND + // 0, //LAND + // 0, //IOR + // 0, //LOR + // 0, //IXOR + // 0, //LXOR + // 8, //IINC + // 0, //I2L + // 0, //I2F + // 0, //I2D + // 0, //L2I + // 0, //L2F + // 0, //L2D + // 0, //F2I + // 0, //F2L + // 0, //F2D + // 0, //D2I + // 0, //D2L + // 0, //D2F + // 0, //I2B + // 0, //I2C + // 0, //I2S + // 0, //LCMP + // 0, //FCMPL + // 0, //FCMPG + // 0, //DCMPL + // 0, //DCMPG + // 6, //IFEQ + // 6, //IFNE + // 6, //IFLT + // 6, //IFGE + // 6, //IFGT + // 6, //IFLE + // 6, //IF_ICMPEQ + // 6, //IF_ICMPNE + // 6, //IF_ICMPLT + // 6, //IF_ICMPGE + // 6, //IF_ICMPGT + // 6, //IF_ICMPLE + // 6, //IF_ACMPEQ + // 6, //IF_ACMPNE + // 6, //GOTO + // 6, //JSR + // 2, //RET + // 9, //TABLESWITCH + // 10, //LOOKUPSWITCH + // 0, //IRETURN + // 0, //LRETURN + // 0, //FRETURN + // 0, //DRETURN + // 0, //ARETURN + // 0, //RETURN + // 4, //GETSTATIC + // 4, //PUTSTATIC + // 4, //GETFIELD + // 4, //PUTFIELD + // 5, //INVOKEVIRTUAL + // 5, //INVOKESPECIAL + // 5, //INVOKESTATIC + // 5, //INVOKEINTERFACE + // -1, //UNUSED + // 3, //NEW + // 1, //NEWARRAY + // 3, //ANEWARRAY + // 0, //ARRAYLENGTH + // 0, //ATHROW + // 3, //CHECKCAST + // 3, //INSTANCEOF + // 0, //MONITORENTER + // 0, //MONITOREXIT + // -1, //WIDE + // 11, //MULTIANEWARRAY + // 6, //IFNULL + // 6, //IFNONNULL + // -1, //GOTO_W + // -1 //JSR_W + // }; + // for (int i = 0; i < TYPE.length; ++i) { + // System.out.print((char)(TYPE[i] + 1 + 'A')); + // } + // System.out.println(); + // } + + /** + * Constructs a new {@link CheckMethodAdapter} object. + * + * @param cv the code visitor to which this adapter must delegate calls. + */ + public CheckMethodAdapter(final MethodVisitor cv) { + super(cv); + this.labels = new HashMap(); + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + checkEndMethod(); + checkDesc(desc, false); + return new CheckAnnotationAdapter(mv.visitAnnotation(desc, visible)); + } + + public AnnotationVisitor visitAnnotationDefault() { + checkEndMethod(); + return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false); + } + + public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible) + { + checkEndMethod(); + checkDesc(desc, false); + return new CheckAnnotationAdapter(mv.visitParameterAnnotation(parameter, + desc, + visible)); + } + + public void visitAttribute(final Attribute attr) { + checkEndMethod(); + if (attr == null) { + throw new IllegalArgumentException("Invalid attribute (must not be null)"); + } + mv.visitAttribute(attr); + } + + public void visitCode() { + startCode = true; + mv.visitCode(); + } + + public void visitInsn(final int opcode) { + checkStartCode(); + checkEndCode(); + checkOpcode(opcode, 0); + mv.visitInsn(opcode); + } + + public void visitIntInsn(final int opcode, final int operand) { + checkStartCode(); + checkEndCode(); + checkOpcode(opcode, 1); + switch (opcode) { + case Opcodes.BIPUSH: + checkSignedByte(operand, "Invalid operand"); + break; + case Opcodes.SIPUSH: + checkSignedShort(operand, "Invalid operand"); + break; + // case Constants.NEWARRAY: + default: + if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) { + throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): " + + operand); + } + } + mv.visitIntInsn(opcode, operand); + } + + public void visitVarInsn(final int opcode, final int var) { + checkStartCode(); + checkEndCode(); + checkOpcode(opcode, 2); + checkUnsignedShort(var, "Invalid variable index"); + mv.visitVarInsn(opcode, var); + } + + public void visitTypeInsn(final int opcode, final String desc) { + checkStartCode(); + checkEndCode(); + checkOpcode(opcode, 3); + if (desc != null && desc.length() > 0 && desc.charAt(0) == '[') { + checkDesc(desc, false); + } else { + checkInternalName(desc, "type"); + } + if (opcode == Opcodes.NEW && desc.charAt(0) == '[') { + throw new IllegalArgumentException("NEW cannot be used to create arrays: " + + desc); + } + mv.visitTypeInsn(opcode, desc); + } + + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + checkStartCode(); + checkEndCode(); + checkOpcode(opcode, 4); + checkInternalName(owner, "owner"); + checkIdentifier(name, "name"); + checkDesc(desc, false); + mv.visitFieldInsn(opcode, owner, name, desc); + } + + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + checkStartCode(); + checkEndCode(); + checkOpcode(opcode, 5); + checkMethodIdentifier(name, "name"); + if (!name.equals("clone")) { + // In JDK1.5, clone method can be called on array class descriptors + checkInternalName(owner, "owner"); + } + checkMethodDesc(desc); + mv.visitMethodInsn(opcode, owner, name, desc); + } + + public void visitJumpInsn(final int opcode, final Label label) { + checkStartCode(); + checkEndCode(); + checkOpcode(opcode, 6); + checkLabel(label, false, "label"); + mv.visitJumpInsn(opcode, label); + } + + public void visitLabel(final Label label) { + checkStartCode(); + checkEndCode(); + checkLabel(label, false, "label"); + if (labels.get(label) != null) { + throw new IllegalArgumentException("Already visited label"); + } else { + labels.put(label, new Integer(labels.size())); + } + mv.visitLabel(label); + } + + public void visitLdcInsn(final Object cst) { + checkStartCode(); + checkEndCode(); + if (!(cst instanceof Type)) { + checkConstant(cst); + } + mv.visitLdcInsn(cst); + } + + public void visitIincInsn(final int var, final int increment) { + checkStartCode(); + checkEndCode(); + checkUnsignedShort(var, "Invalid variable index"); + checkSignedShort(increment, "Invalid increment"); + mv.visitIincInsn(var, increment); + } + + public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + checkStartCode(); + checkEndCode(); + if (max < min) { + throw new IllegalArgumentException("Max = " + max + + " must be greater than or equal to min = " + min); + } + checkLabel(dflt, false, "default label"); + if (labels == null || labels.length != max - min + 1) { + throw new IllegalArgumentException("There must be max - min + 1 labels"); + } + for (int i = 0; i < labels.length; ++i) { + checkLabel(labels[i], false, "label at index " + i); + } + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + + public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]) + { + checkEndCode(); + checkStartCode(); + checkLabel(dflt, false, "default label"); + if (keys == null || labels == null || keys.length != labels.length) { + throw new IllegalArgumentException("There must be the same number of keys and labels"); + } + for (int i = 0; i < labels.length; ++i) { + checkLabel(labels[i], false, "label at index " + i); + } + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + + public void visitMultiANewArrayInsn(final String desc, final int dims) { + checkStartCode(); + checkEndCode(); + checkDesc(desc, false); + if (desc.charAt(0) != '[') { + throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): " + + desc); + } + if (dims < 1) { + throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): " + + dims); + } + if (dims > desc.lastIndexOf('[') + 1) { + throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): " + + dims); + } + mv.visitMultiANewArrayInsn(desc, dims); + } + + public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type) + { + checkStartCode(); + checkEndCode(); + if (type != null) { + checkInternalName(type, "type"); + } + mv.visitTryCatchBlock(start, end, handler, type); + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + checkStartCode(); + checkEndCode(); + checkIdentifier(name, "name"); + checkDesc(desc, false); + checkLabel(start, true, "start label"); + checkLabel(end, true, "end label"); + checkUnsignedShort(index, "Invalid variable index"); + int s = ((Integer) labels.get(start)).intValue(); + int e = ((Integer) labels.get(end)).intValue(); + if (e < s) { + throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)"); + } + mv.visitLocalVariable(name, desc, signature, start, end, index); + } + + public void visitLineNumber(final int line, final Label start) { + checkStartCode(); + checkEndCode(); + checkUnsignedShort(line, "Invalid line number"); + checkLabel(start, true, "start label"); + mv.visitLineNumber(line, start); + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + checkStartCode(); + checkEndCode(); + endCode = true; + checkUnsignedShort(maxStack, "Invalid max stack"); + checkUnsignedShort(maxLocals, "Invalid max locals"); + mv.visitMaxs(maxStack, maxLocals); + } + + public void visitEnd() { + checkEndMethod(); + endMethod = true; + mv.visitEnd(); + } + + // ------------------------------------------------------------------------- + + /** + * Checks that the visitCode method has been called. + */ + void checkStartCode() { + if (!startCode) { + throw new IllegalStateException("Cannot visit instructions before visitCode has been called."); + } + } + + /** + * Checks that the visitMaxs method has not been called. + */ + void checkEndCode() { + if (endCode) { + throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called."); + } + } + + /** + * Checks that the visitEnd method has not been called. + */ + void checkEndMethod() { + if (endMethod) { + throw new IllegalStateException("Cannot visit elements after visitEnd has been called."); + } + } + + /** + * Checks that the type of the given opcode is equal to the given type. + * + * @param opcode the opcode to be checked. + * @param type the expected opcode type. + */ + static void checkOpcode(final int opcode, final int type) { + if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) { + throw new IllegalArgumentException("Invalid opcode: " + opcode); + } + } + + /** + * Checks that the given value is a signed byte. + * + * @param value the value to be checked. + * @param msg an message to be used in case of error. + */ + static void checkSignedByte(final int value, final String msg) { + if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) { + throw new IllegalArgumentException(msg + + " (must be a signed byte): " + value); + } + } + + /** + * Checks that the given value is a signed short. + * + * @param value the value to be checked. + * @param msg an message to be used in case of error. + */ + static void checkSignedShort(final int value, final String msg) { + if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { + throw new IllegalArgumentException(msg + + " (must be a signed short): " + value); + } + } + + /** + * Checks that the given value is an unsigned short. + * + * @param value the value to be checked. + * @param msg an message to be used in case of error. + */ + static void checkUnsignedShort(final int value, final String msg) { + if (value < 0 || value > 65535) { + throw new IllegalArgumentException(msg + + " (must be an unsigned short): " + value); + } + } + + /** + * Checks that the given value is an {@link Integer}, a{@link Float}, a + * {@link Long}, a {@link Double} or a {@link String}. + * + * @param cst the value to be checked. + */ + static void checkConstant(final Object cst) { + if (!(cst instanceof Integer) && !(cst instanceof Float) + && !(cst instanceof Long) && !(cst instanceof Double) + && !(cst instanceof String)) + { + throw new IllegalArgumentException("Invalid constant: " + cst); + } + } + + /** + * Checks that the given string is a valid Java identifier. + * + * @param name the string to be checked. + * @param msg a message to be used in case of error. + */ + static void checkIdentifier(final String name, final String msg) { + checkIdentifier(name, 0, -1, msg); + } + + /** + * Checks that the given substring is a valid Java identifier. + * + * @param name the string to be checked. + * @param start index of the first character of the identifier (inclusive). + * @param end index of the last character of the identifier (exclusive). -1 + * is equivalent to <tt>name.length()</tt> if name is not + * <tt>null</tt>. + * @param msg a message to be used in case of error. + */ + static void checkIdentifier( + final String name, + final int start, + final int end, + final String msg) + { + if (name == null || (end == -1 ? name.length() <= start : end <= start)) + { + throw new IllegalArgumentException("Invalid " + msg + + " (must not be null or empty)"); + } + if (!Character.isJavaIdentifierStart(name.charAt(start))) { + throw new IllegalArgumentException("Invalid " + msg + + " (must be a valid Java identifier): " + name); + } + int max = (end == -1 ? name.length() : end); + for (int i = start + 1; i < max; ++i) { + if (!Character.isJavaIdentifierPart(name.charAt(i))) { + throw new IllegalArgumentException("Invalid " + msg + + " (must be a valid Java identifier): " + name); + } + } + } + + /** + * Checks that the given string is a valid Java identifier or is equal to + * '<init>' or '<clinit>'. + * + * @param name the string to be checked. + * @param msg a message to be used in case of error. + */ + static void checkMethodIdentifier(final String name, final String msg) { + if (name == null || name.length() == 0) { + throw new IllegalArgumentException("Invalid " + msg + + " (must not be null or empty)"); + } + if (name.equals("<init>") || name.equals("<clinit>")) { + return; + } + if (!Character.isJavaIdentifierStart(name.charAt(0))) { + throw new IllegalArgumentException("Invalid " + + msg + + " (must be a '<init>', '<clinit>' or a valid Java identifier): " + + name); + } + for (int i = 1; i < name.length(); ++i) { + if (!Character.isJavaIdentifierPart(name.charAt(i))) { + throw new IllegalArgumentException("Invalid " + + msg + + " (must be '<init>' or '<clinit>' or a valid Java identifier): " + + name); + } + } + } + + /** + * Checks that the given string is a valid internal class name. + * + * @param name the string to be checked. + * @param msg a message to be used in case of error. + */ + static void checkInternalName(final String name, final String msg) { + checkInternalName(name, 0, -1, msg); + } + + /** + * Checks that the given substring is a valid internal class name. + * + * @param name the string to be checked. + * @param start index of the first character of the identifier (inclusive). + * @param end index of the last character of the identifier (exclusive). -1 + * is equivalent to <tt>name.length()</tt> if name is not + * <tt>null</tt>. + * @param msg a message to be used in case of error. + */ + static void checkInternalName( + final String name, + final int start, + final int end, + final String msg) + { + if (name == null || name.length() == 0) { + throw new IllegalArgumentException("Invalid " + msg + + " (must not be null or empty)"); + } + int max = (end == -1 ? name.length() : end); + try { + int begin = start; + int slash; + do { + slash = name.indexOf('/', begin + 1); + if (slash == -1 || slash > max) { + slash = max; + } + checkIdentifier(name, begin, slash, null); + begin = slash + 1; + } while (slash != max); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("Invalid " + + msg + + " (must be a fully qualified class name in internal form): " + + name); + } + } + + /** + * Checks that the given string is a valid type descriptor. + * + * @param desc the string to be checked. + * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. + */ + static void checkDesc(final String desc, final boolean canBeVoid) { + int end = checkDesc(desc, 0, canBeVoid); + if (end != desc.length()) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + } + + /** + * Checks that a the given substring is a valid type descriptor. + * + * @param desc the string to be checked. + * @param start index of the first character of the identifier (inclusive). + * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid. + * @return the index of the last character of the type decriptor, plus one. + */ + static int checkDesc( + final String desc, + final int start, + final boolean canBeVoid) + { + if (desc == null || start >= desc.length()) { + throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)"); + } + int index; + switch (desc.charAt(start)) { + case 'V': + if (canBeVoid) { + return start + 1; + } else { + throw new IllegalArgumentException("Invalid descriptor: " + + desc); + } + case 'Z': + case 'C': + case 'B': + case 'S': + case 'I': + case 'F': + case 'J': + case 'D': + return start + 1; + case '[': + index = start + 1; + while (index < desc.length() && desc.charAt(index) == '[') { + ++index; + } + if (index < desc.length()) { + return checkDesc(desc, index, false); + } else { + throw new IllegalArgumentException("Invalid descriptor: " + + desc); + } + case 'L': + index = desc.indexOf(';', start); + if (index == -1 || index - start < 2) { + throw new IllegalArgumentException("Invalid descriptor: " + + desc); + } + try { + checkInternalName(desc, start + 1, index, null); + } catch (IllegalArgumentException ex) { + throw new IllegalArgumentException("Invalid descriptor: " + + desc); + } + return index + 1; + default: + throw new IllegalArgumentException("Invalid descriptor: " + + desc); + } + } + + /** + * Checks that the given string is a valid method descriptor. + * + * @param desc the string to be checked. + */ + static void checkMethodDesc(final String desc) { + if (desc == null || desc.length() == 0) { + throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)"); + } + if (desc.charAt(0) != '(' || desc.length() < 3) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + int start = 1; + if (desc.charAt(start) != ')') { + do { + if (desc.charAt(start) == 'V') { + throw new IllegalArgumentException("Invalid descriptor: " + + desc); + } + start = checkDesc(desc, start, false); + } while (start < desc.length() && desc.charAt(start) != ')'); + } + start = checkDesc(desc, start + 1, true); + if (start != desc.length()) { + throw new IllegalArgumentException("Invalid descriptor: " + desc); + } + } + + /** + * Checks that the given label is not null. This method can also check that + * the label has been visited. + * + * @param label the label to be checked. + * @param checkVisited <tt>true</tt> to check that the label has been + * visited. + * @param msg a message to be used in case of error. + */ + void checkLabel( + final Label label, + final boolean checkVisited, + final String msg) + { + if (label == null) { + throw new IllegalArgumentException("Invalid " + msg + + " (must not be null)"); + } + if (checkVisited && labels.get(label) == null) { + throw new IllegalArgumentException("Invalid " + msg + + " (must be visited first)"); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/TraceAbstractVisitor.java b/asmx/src/org/objectweb/asm/util/TraceAbstractVisitor.java new file mode 100644 index 0000000..75bca94 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/TraceAbstractVisitor.java
@@ -0,0 +1,205 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.util.attrs.Traceable; + +/** + * An abstract trace visitor. + * + * @author Eric Bruneton + */ +public abstract class TraceAbstractVisitor extends AbstractVisitor { + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for internal + * type names in bytecode notation. + */ + public final static int INTERNAL_NAME = 0; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for field + * descriptors, formatted in bytecode notation + */ + public final static int FIELD_DESCRIPTOR = 1; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for field + * signatures, formatted in bytecode notation + */ + public final static int FIELD_SIGNATURE = 2; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for method + * descriptors, formatted in bytecode notation + */ + public final static int METHOD_DESCRIPTOR = 3; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for method + * signatures, formatted in bytecode notation + */ + public final static int METHOD_SIGNATURE = 4; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for class + * signatures, formatted in bytecode notation + */ + public final static int CLASS_SIGNATURE = 5; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for field or + * method return value signatures, formatted in default Java notation + * (non-bytecode) + */ + public final static int TYPE_DECLARATION = 6; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for class + * signatures, formatted in default Java notation (non-bytecode) + */ + public final static int CLASS_DECLARATION = 7; + + /** + * Constant used in {@link #appendDescriptor appendDescriptor} for method + * parameter signatures, formatted in default Java notation (non-bytecode) + */ + public final static int PARAMETERS_DECLARATION = 8; + + /** + * Tab for class members. + */ + protected String tab = " "; + + /** + * Prints a disassembled view of the given annotation. + * + * @param desc the class descriptor of the annotation class. + * @param visible <tt>true</tt> if the annotation is visible at runtime. + * @return a visitor to visit the annotation values. + */ + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + buf.setLength(0); + buf.append(tab).append('@'); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + TraceAnnotationVisitor tav = createTraceAnnotationVisitor(); + text.add(tav.getText()); + text.add(visible ? ")\n" : ") // invisible\n"); + return tav; + } + + //jaime + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible) + { + buf.setLength(0); + buf.append(tab).append('@'); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + TraceTypeAnnotationVisitor txav = + createTraceTypeAnnotationVisitor(); + text.add(txav.getText()); + text.add(visible ? ")\n" : ") // invisible\n"); + return txav; + } + //end jaime + + /** + * Prints a disassembled view of the given attribute. + * + * @param attr an attribute. + */ + public void visitAttribute(final Attribute attr) { + buf.setLength(0); + buf.append(tab).append("ATTRIBUTE "); + appendDescriptor(-1, attr.type); + + if (attr instanceof Traceable) { + ((Traceable) attr).trace(buf, null); + } else { + buf.append(" : ").append(attr.toString()).append("\n"); + } + + text.add(buf.toString()); + } + + /** + * Does nothing. + */ + public void visitEnd() { + // does nothing + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + protected TraceAnnotationVisitor createTraceAnnotationVisitor() { + return new TraceAnnotationVisitor(); + } + + //jaime + protected TraceTypeAnnotationVisitor createTraceTypeAnnotationVisitor() { + return new TraceTypeAnnotationVisitor(); + } + //end jaime + + /** + * 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, ... + * @param desc an internal name, type descriptor, or type signature. May be + * <tt>null</tt>. + */ + protected void appendDescriptor(final int type, final String desc) { + if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE + || type == METHOD_SIGNATURE) + { + if (desc != null) { + buf.append("// signature ").append(desc).append('\n'); + } + } else { + buf.append(desc); + } + } + +}
diff --git a/asmx/src/org/objectweb/asm/util/TraceAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/TraceAnnotationVisitor.java new file mode 100644 index 0000000..827225b --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/TraceAnnotationVisitor.java
@@ -0,0 +1,272 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Type; + +/** + * An {@link AnnotationVisitor} that prints a disassembled view of the + * annotations it visits. + * + * @author Eric Bruneton + */ +public class TraceAnnotationVisitor extends TraceAbstractVisitor implements + AnnotationVisitor +{ + + /** + * The {@link AnnotationVisitor} to which this visitor delegates calls. May + * be <tt>null</tt>. + */ + protected AnnotationVisitor av; + + private int valueNumber = 0; + + /** + * Constructs a new {@link TraceAnnotationVisitor}. + */ + public TraceAnnotationVisitor() { + // ignore + } + + // ------------------------------------------------------------------------ + // Implementation of the AnnotationVisitor interface + // ------------------------------------------------------------------------ + + public void visit(final String name, final Object value) { + buf.setLength(0); + appendComa(valueNumber++); + + if (name != null) { + buf.append(name).append('='); + } + + if (value instanceof String) { + visitString((String) value); + } else if (value instanceof Type) { + visitType((Type) value); + } else if (value instanceof Byte) { + visitByte(((Byte) value).byteValue()); + } else if (value instanceof Boolean) { + visitBoolean(((Boolean) value).booleanValue()); + } else if (value instanceof Short) { + visitShort(((Short) value).shortValue()); + } else if (value instanceof Character) { + visitChar(((Character) value).charValue()); + } else if (value instanceof Integer) { + visitInt(((Integer) value).intValue()); + } else if (value instanceof Float) { + visitFloat(((Float) value).floatValue()); + } else if (value instanceof Long) { + visitLong(((Long) value).longValue()); + } else if (value instanceof Double) { + visitDouble(((Double) value).doubleValue()); + } else if (value.getClass().isArray()) { + buf.append('{'); + if (value instanceof byte[]) { + byte[] v = (byte[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitByte(v[i]); + } + } else if (value instanceof boolean[]) { + boolean[] v = (boolean[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitBoolean(v[i]); + } + } else if (value instanceof short[]) { + short[] v = (short[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitShort(v[i]); + } + } else if (value instanceof char[]) { + char[] v = (char[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitChar(v[i]); + } + } else if (value instanceof int[]) { + int[] v = (int[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitInt(v[i]); + } + } else if (value instanceof long[]) { + long[] v = (long[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitLong(v[i]); + } + } else if (value instanceof float[]) { + float[] v = (float[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitFloat(v[i]); + } + } else if (value instanceof double[]) { + double[] v = (double[]) value; + for (int i = 0; i < v.length; i++) { + appendComa(i); + visitDouble(v[i]); + } + } + buf.append('}'); + } else { + buf.append(value); + } + + text.add(buf.toString()); + + if (av != null) { + av.visit(name, value); + } + } + + private void visitInt(int value) { + buf.append(value); + } + + private void visitLong(long value) { + buf.append(value).append('L'); + } + + private void visitFloat(float value) { + buf.append(value).append('F'); + } + + private void visitDouble(double value) { + buf.append(value).append('D'); + } + + private void visitChar(char value) { + buf.append("(char)").append((int) value); + } + + private void visitShort(short value) { + buf.append("(short)").append(value); + } + + private void visitByte(byte value) { + buf.append("(byte)").append(value); + } + + private void visitBoolean(boolean value) { + buf.append(value); + } + + private void visitString(String value) { + appendString(buf, value); + } + + private void visitType(Type value) { + buf.append(value.getClassName()).append(".class"); + } + + public void visitEnum( + final String name, + final String desc, + final String value) + { + buf.setLength(0); + appendComa(valueNumber++); + if (name != null) { + buf.append(name).append('='); + } + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('.').append(value); + text.add(buf.toString()); + + if (av != null) { + av.visitEnum(name, desc, value); + } + } + + public AnnotationVisitor visitAnnotation( + final String name, + final String desc) + { + buf.setLength(0); + appendComa(valueNumber++); + if (name != null) { + buf.append(name).append('='); + } + buf.append('@'); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + TraceAnnotationVisitor tav = createTraceAnnotationVisitor(); + text.add(tav.getText()); + text.add(")"); + if (av != null) { + tav.av = av.visitAnnotation(name, desc); + } + return tav; + } + + public AnnotationVisitor visitArray(final String name) { + buf.setLength(0); + appendComa(valueNumber++); + if (name != null) { + buf.append(name).append('='); + } + buf.append('{'); + text.add(buf.toString()); + TraceAnnotationVisitor tav = createTraceAnnotationVisitor(); + text.add(tav.getText()); + text.add("}"); + if (av != null) { + tav.av = av.visitArray(name); + } + return tav; + } + + public void visitEnd() { + if (av != null) { + av.visitEnd(); + } + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + protected TraceAnnotationVisitor createTraceAnnotationVisitor() { + return new TraceAnnotationVisitor(); + } + + private void appendComa(int i) { + if (i != 0) { + buf.append(", "); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/TraceClassVisitor.java b/asmx/src/org/objectweb/asm/util/TraceClassVisitor.java new file mode 100644 index 0000000..3055ffd --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/TraceClassVisitor.java
@@ -0,0 +1,550 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import java.io.FileInputStream; +import java.io.PrintWriter; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.signature.SignatureReader; + +/** + * A {@link ClassVisitor} that prints a disassembled view of the classes it + * visits. This class visitor can be used alone (see the {@link #main main} + * method) to disassemble a class. It can also be used in the middle of class + * visitor chain to trace the class that is visited at a given point in this + * chain. This may be uselful for debugging purposes. <p> The trace printed when + * visiting the <tt>Hello</tt> class is the following: <p> <blockquote> + * + * <pre> + * // class version 49.0 (49) + * // access flags 33 + * public class Hello { + * + * // compiled from: Hello.java + * + * // access flags 1 + * public <init> ()V + * ALOAD 0 + * INVOKESPECIAL java/lang/Object <init> ()V + * RETURN + * MAXSTACK = 1 + * MAXLOCALS = 1 + * + * // access flags 9 + * public static main ([Ljava/lang/String;)V + * GETSTATIC java/lang/System out Ljava/io/PrintStream; + * LDC "hello" + * INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V + * RETURN + * MAXSTACK = 2 + * MAXLOCALS = 1 + * } + * </pre> + * + * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote> + * + * <pre> + * public class Hello { + * + * public static void main(String[] args) { + * System.out.println("hello"); + * } + * } + * </pre> + * + * </blockquote> + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class TraceClassVisitor extends TraceAbstractVisitor implements + ClassVisitor +{ + + /** + * The {@link ClassVisitor} to which this visitor delegates calls. May be + * <tt>null</tt>. + */ + protected final ClassVisitor cv; + + /** + * The print writer to be used to print the class. + */ + protected final PrintWriter pw; + + /** + * Prints a disassembled view of the given class to the standard output. <p> + * Usage: TraceClassVisitor [-debug] <fully qualified 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. + */ + public static void main(final String[] args) throws Exception { + int i = 0; + boolean skipDebug = true; + + boolean ok = true; + if (args.length < 1 || args.length > 2) { + ok = false; + } + if (ok && args[0].equals("-debug")) { + i = 1; + skipDebug = false; + if (args.length != 2) { + ok = false; + } + } + if (!ok) { + System.err.println("Prints a disassembled view of the given class."); + System.err.println("Usage: TraceClassVisitor [-debug] " + + "<fully qualified class name or class file name>"); + return; + } + ClassReader cr; + if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1 + || args[i].indexOf('/') > -1) + { + cr = new ClassReader(new FileInputStream(args[i])); + } else { + cr = new ClassReader(args[i]); + } + cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), + getDefaultAttributes(), + skipDebug); + } + + /** + * Constructs a new {@link TraceClassVisitor}. + * + * @param pw the print writer to be used to print the class. + */ + public TraceClassVisitor(final PrintWriter pw) { + this(null, pw); + } + + /** + * Constructs a new {@link TraceClassVisitor}. + * + * @param cv the {@link ClassVisitor} to which this visitor delegates calls. + * May be <tt>null</tt>. + * @param pw the print writer to be used to print the class. + */ + public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) { + this.cv = cv; + this.pw = pw; + } + + // ------------------------------------------------------------------------ + // Implementation of the ClassVisitor interface + // ------------------------------------------------------------------------ + + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) + { + int major = version & 0xFFFF; + int minor = version >>> 16; + buf.setLength(0); + buf.append("// class version ") + .append(major) + .append('.') + .append(minor) + .append(" (") + .append(version) + .append(")\n"); + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + buf.append("// DEPRECATED\n"); + } + buf.append("// access flags ").append(access).append('\n'); + + appendDescriptor(CLASS_SIGNATURE, signature); + if (signature != null) { + TraceSignatureVisitor sv = new TraceSignatureVisitor(access); + SignatureReader r = new SignatureReader(signature); + r.accept(sv); + buf.append("// declaration: ") + .append(name) + .append(sv.getDeclaration()) + .append('\n'); + } + + appendAccess(access & ~Opcodes.ACC_SUPER); + if ((access & Opcodes.ACC_ANNOTATION) != 0) { + buf.append("@interface "); + } else if ((access & Opcodes.ACC_INTERFACE) != 0) { + buf.append("interface "); + } else if ((access & Opcodes.ACC_ENUM) != 0) { + buf.append("enum "); + } else { + buf.append("class "); + } + appendDescriptor(INTERNAL_NAME, name); + + if (superName != null && !superName.equals("java/lang/Object")) { + buf.append(" extends "); + appendDescriptor(INTERNAL_NAME, superName); + buf.append(' '); + } + if (interfaces != null && interfaces.length > 0) { + buf.append(" implements "); + for (int i = 0; i < interfaces.length; ++i) { + appendDescriptor(INTERNAL_NAME, interfaces[i]); + buf.append(' '); + } + } + buf.append(" {\n\n"); + + text.add(buf.toString()); + + if (cv != null) { + cv.visit(version, access, name, signature, superName, interfaces); + } + } + + public void visitSource(final String file, final String debug) { + buf.setLength(0); + if (file != null) { + buf.append(tab) + .append("// compiled from: ") + .append(file) + .append('\n'); + } + if (debug != null) { + buf.append(tab) + .append("// debug info: ") + .append(debug) + .append('\n'); + } + if (buf.length() > 0) { + text.add(buf.toString()); + } + + if (cv != null) { + cv.visitSource(file, debug); + } + } + + public void visitOuterClass( + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + buf.append(tab).append("OUTERCLASS "); + appendDescriptor(INTERNAL_NAME, owner); + // if enclosing name is null, so why should we show this info? + if (name != null) { + buf.append(' ').append(name).append(' '); + } else { + buf.append(' '); + } + appendDescriptor(METHOD_DESCRIPTOR, desc); + buf.append('\n'); + text.add(buf.toString()); + + if (cv != null) { + cv.visitOuterClass(owner, name, desc); + } + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + text.add("\n"); + AnnotationVisitor tav = super.visitAnnotation(desc, visible); + if (cv != null) { + ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(desc, + visible); + } + return tav; + } + + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + text.add("\n"); + TypeAnnotationVisitor txav = + super.visitTypeAnnotation(desc, visible); + if (cv != null) { + ((TraceTypeAnnotationVisitor) txav).xav = + cv.visitTypeAnnotation(desc, visible, inCode); + } + return txav; + } + + public void visitAttribute(final Attribute attr) { + text.add("\n"); + super.visitAttribute(attr); + + if (cv != null) { + cv.visitAttribute(attr); + } + } + + public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access) + { + buf.setLength(0); + buf.append(tab).append("// access flags ").append(access + & ~Opcodes.ACC_SUPER).append('\n'); + buf.append(tab); + appendAccess(access); + buf.append("INNERCLASS "); + if ((access & Opcodes.ACC_ENUM) != 0) { + buf.append("enum "); + } + appendDescriptor(INTERNAL_NAME, name); + buf.append(' '); + appendDescriptor(INTERNAL_NAME, outerName); + buf.append(' '); + appendDescriptor(INTERNAL_NAME, innerName); + buf.append('\n'); + text.add(buf.toString()); + + if (cv != null) { + cv.visitInnerClass(name, outerName, innerName, access); + } + } + + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + buf.setLength(0); + buf.append('\n'); + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + buf.append(tab).append("// DEPRECATED\n"); + } + buf.append(tab).append("// access flags ").append(access).append('\n'); + if (signature != null) { + buf.append(tab); + appendDescriptor(FIELD_SIGNATURE, signature); + + TraceSignatureVisitor sv = new TraceSignatureVisitor(0); + SignatureReader r = new SignatureReader(signature); + r.acceptType(sv); + buf.append(tab) + .append("// declaration: ") + .append(sv.getDeclaration()) + .append('\n'); + } + + buf.append(tab); + appendAccess(access); + if ((access & Opcodes.ACC_ENUM) != 0) { + buf.append("enum "); + } + + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append(' ').append(name); + if (value != null) { + buf.append(" = "); + if (value instanceof String) { + buf.append("\"").append(value).append("\""); + } else { + buf.append(value); + } + } + + buf.append('\n'); + text.add(buf.toString()); + + TraceFieldVisitor tav = createTraceFieldVisitor(); + text.add(tav.getText()); + + if (cv != null) { + tav.fv = cv.visitField(access, name, desc, signature, value); + } + + return tav; + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + buf.setLength(0); + buf.append('\n'); + if ((access & Opcodes.ACC_DEPRECATED) != 0) { + buf.append(tab).append("// DEPRECATED\n"); + } + buf.append(tab).append("// access flags ").append(access).append('\n'); + buf.append(tab); + appendDescriptor(METHOD_SIGNATURE, signature); + + if (signature != null) { + TraceSignatureVisitor v = new TraceSignatureVisitor(0); + SignatureReader r = new SignatureReader(signature); + r.accept(v); + String genericDecl = v.getDeclaration(); + String genericReturn = v.getReturnType(); + String genericExceptions = v.getExceptions(); + + buf.append(tab) + .append("// declaration: ") + .append(genericReturn) + .append(' ') + .append(name) + .append(genericDecl); + if (genericExceptions != null) { + buf.append(" throws ").append(genericExceptions); + } + buf.append('\n'); + } + + appendAccess(access); + if ((access & Opcodes.ACC_NATIVE) != 0) { + buf.append("native "); + } + if ((access & Opcodes.ACC_VARARGS) != 0) { + buf.append("varargs "); + } + if ((access & Opcodes.ACC_BRIDGE) != 0) { + buf.append("bridge "); + } + + buf.append(name); + appendDescriptor(METHOD_DESCRIPTOR, desc); + if (exceptions != null && exceptions.length > 0) { + buf.append(" throws "); + for (int i = 0; i < exceptions.length; ++i) { + appendDescriptor(INTERNAL_NAME, exceptions[i]); + buf.append(' '); + } + } + + buf.append('\n'); + text.add(buf.toString()); + + TraceMethodVisitor tcv = createTraceMethodVisitor(); + text.add(tcv.getText()); + + if (cv != null) { + tcv.mv = cv.visitMethod(access, name, desc, signature, exceptions); + } + + return tcv; + } + + public void visitEnd() { + text.add("}\n"); + + printList(pw, text); + pw.flush(); + + if (cv != null) { + cv.visitEnd(); + } + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + protected TraceFieldVisitor createTraceFieldVisitor() { + return new TraceFieldVisitor(); + } + + protected TraceMethodVisitor createTraceMethodVisitor() { + return new TraceMethodVisitor(); + } + + /** + * Appends a string representation of the given access modifiers to {@link + * #buf buf}. + * + * @param access some access modifiers. + */ + private void appendAccess(final int access) { + if ((access & Opcodes.ACC_PUBLIC) != 0) { + buf.append("public "); + } + if ((access & Opcodes.ACC_PRIVATE) != 0) { + buf.append("private "); + } + if ((access & Opcodes.ACC_PROTECTED) != 0) { + buf.append("protected "); + } + if ((access & Opcodes.ACC_FINAL) != 0) { + buf.append("final "); + } + if ((access & Opcodes.ACC_STATIC) != 0) { + buf.append("static "); + } + if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { + buf.append("synchronized "); + } + if ((access & Opcodes.ACC_VOLATILE) != 0) { + buf.append("volatile "); + } + if ((access & Opcodes.ACC_TRANSIENT) != 0) { + buf.append("transient "); + } + // if ((access & Constants.ACC_NATIVE) != 0) { + // buf.append("native "); + // } + if ((access & Opcodes.ACC_ABSTRACT) != 0) { + buf.append("abstract "); + } + if ((access & Opcodes.ACC_STRICT) != 0) { + buf.append("strictfp "); + } + if ((access & Opcodes.ACC_SYNTHETIC) != 0) { + buf.append("synthetic "); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/TraceFieldVisitor.java b/asmx/src/org/objectweb/asm/util/TraceFieldVisitor.java new file mode 100644 index 0000000..d90f1eb --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/TraceFieldVisitor.java
@@ -0,0 +1,93 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; + +/** + * A {@link FieldVisitor} that prints a disassembled view of the fields it + * visits. + * + * @author Eric Bruneton + */ +public class TraceFieldVisitor extends TraceAbstractVisitor implements + FieldVisitor +{ + + /** + * The {@link FieldVisitor} to which this visitor delegates calls. May be + * <tt>null</tt>. + */ + protected FieldVisitor fv; + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + AnnotationVisitor av = super.visitAnnotation(desc, visible); + if (fv != null) { + ((TraceAnnotationVisitor) av).av = fv.visitAnnotation(desc, visible); + } + return av; + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + TypeAnnotationVisitor xav = super.visitTypeAnnotation(desc, visible); + if (fv != null) { + ((TraceTypeAnnotationVisitor) xav).xav = fv.visitTypeAnnotation(desc, visible, inCode); + } + return xav; + } + //end jaime + + public void visitAttribute(final Attribute attr) { + super.visitAttribute(attr); + + if (fv != null) { + fv.visitAttribute(attr); + } + } + + public void visitEnd() { + super.visitEnd(); + + if (fv != null) { + fv.visitEnd(); + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/TraceMethodVisitor.java b/asmx/src/org/objectweb/asm/util/TraceMethodVisitor.java new file mode 100644 index 0000000..da81ada --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/TraceMethodVisitor.java
@@ -0,0 +1,517 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import java.util.HashMap; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.Handle; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.TypePath; +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.util.attrs.Traceable; + +/** + * A {@link MethodVisitor} that prints a disassembled view of the methods it + * visits. + * + * @author Eric Bruneton + */ +public class TraceMethodVisitor extends TraceAbstractVisitor implements + MethodVisitor +{ + /** + * The {@link MethodVisitor} to which this visitor delegates calls. May be + * <tt>null</tt>. + */ + protected MethodVisitor mv; + + /** + * Tab for bytecode instructions. + */ + protected String tab2 = " "; + + /** + * Tab for table and lookup switch instructions. + */ + protected String tab3 = " "; + + /** + * Tab for labels. + */ + protected String ltab = " "; + + /** + * The label names. This map associate String values to Label keys. + */ + protected final HashMap labelNames; + + /** + * Constructs a new {@link TraceMethodVisitor}. + */ + public TraceMethodVisitor() { + this(null); + } + + /** + * Constructs a new {@link TraceMethodVisitor}. + * + * @param mv the {@link MethodVisitor} to which this visitor delegates + * calls. May be <tt>null</tt>. + */ + public TraceMethodVisitor(final MethodVisitor mv) { + this.labelNames = new HashMap(); + this.mv = mv; + } + + // ------------------------------------------------------------------------ + // Implementation of the MethodVisitor interface + // ------------------------------------------------------------------------ + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + AnnotationVisitor av = super.visitAnnotation(desc, visible); + if (mv != null) { + ((TraceAnnotationVisitor) av).av = mv.visitAnnotation(desc, visible); + } + return av; + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible, + final boolean inCode) + { + TypeAnnotationVisitor xav = super.visitTypeAnnotation(desc, visible); + if (mv != null) { + ((TraceTypeAnnotationVisitor) xav).xav = + mv.visitTypeAnnotation(desc, visible, inCode); + } + return xav; + } + //end jaime + + public void visitAttribute(final Attribute attr) { + buf.setLength(0); + buf.append(tab).append("ATTRIBUTE "); + appendDescriptor(-1, attr.type); + + if (attr instanceof Traceable) { + ((Traceable) attr).trace(buf, labelNames); + } else { + buf.append(" : ").append(attr.toString()).append("\n"); + } + + text.add(buf.toString()); + if (mv != null) { + mv.visitAttribute(attr); + } + } + + public AnnotationVisitor visitAnnotationDefault() { + text.add(tab2 + "default="); + TraceAnnotationVisitor tav = new TraceAnnotationVisitor(); + text.add(tav.getText()); + text.add("\n"); + if (mv != null) { + tav.av = mv.visitAnnotationDefault(); + } + return tav; + } + + public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible) + { + buf.setLength(0); + buf.append(tab2).append('@'); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('('); + text.add(buf.toString()); + TraceAnnotationVisitor tav = new TraceAnnotationVisitor(); + text.add(tav.getText()); + text.add(visible ? ") // parameter " : ") // invisible, parameter "); + text.add(new Integer(parameter)); + text.add("\n"); + if (mv != null) { + tav.av = mv.visitParameterAnnotation(parameter, desc, visible); + } + return tav; + } + + public void visitCode() { + if (mv != null) { + mv.visitCode(); + } + } + + public void visitInsn(final int opcode) { + buf.setLength(0); + buf.append(tab2).append(OPCODES[opcode]).append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitInsn(opcode); + } + } + + public void visitIntInsn(final int opcode, final int operand) { + buf.setLength(0); + buf.append(tab2) + .append(OPCODES[opcode]) + .append(' ') + .append(opcode == Opcodes.NEWARRAY + ? TYPES[operand] + : Integer.toString(operand)) + .append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitIntInsn(opcode, operand); + } + } + + public void visitVarInsn(final int opcode, final int var) { + buf.setLength(0); + buf.append(tab2) + .append(OPCODES[opcode]) + .append(' ') + .append(var) + .append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitVarInsn(opcode, var); + } + } + + public void visitTypeInsn(final int opcode, final String desc) { + buf.setLength(0); + buf.append(tab2).append(OPCODES[opcode]).append(' '); + if (desc.startsWith("[")) { + appendDescriptor(FIELD_DESCRIPTOR, desc); + } else { + appendDescriptor(INTERNAL_NAME, desc); + } + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitTypeInsn(opcode, desc); + } + } + + public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + buf.append(tab2).append(OPCODES[opcode]).append(' '); + appendDescriptor(INTERNAL_NAME, owner); + buf.append('.').append(name).append(" : "); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitFieldInsn(opcode, owner, name, desc); + } + } + + public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc) + { + buf.setLength(0); + buf.append(tab2).append(OPCODES[opcode]).append(' '); + appendDescriptor(INTERNAL_NAME, owner); + buf.append('.').append(name).append(' '); + appendDescriptor(METHOD_DESCRIPTOR, desc); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitMethodInsn(opcode, owner, name, desc); + } + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + // TODO Auto-generated method stub + + } + + @Override + public AnnotationVisitor visitInsnAnnotation(int typeRef, + TypePath typePath, String desc, boolean visible) { + // TODO Auto-generated method stub + return null; + } + + public void visitJumpInsn(final int opcode, final Label label) { + buf.setLength(0); + buf.append(tab2).append(OPCODES[opcode]).append(' '); + appendLabel(label); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitJumpInsn(opcode, label); + } + } + + public void visitLabel(final Label label) { + buf.setLength(0); + buf.append(ltab); + appendLabel(label); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitLabel(label); + } + } + + public void visitLdcInsn(final Object cst) { + buf.setLength(0); + buf.append(tab2).append("LDC "); + if (cst instanceof String) { + AbstractVisitor.appendString(buf, (String) cst); + } else if (cst instanceof Type) { + buf.append(((Type) cst).getDescriptor() + ".class"); + } else { + buf.append(cst); + } + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitLdcInsn(cst); + } + } + + public void visitIincInsn(final int var, final int increment) { + buf.setLength(0); + buf.append(tab2) + .append("IINC ") + .append(var) + .append(' ') + .append(increment) + .append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitIincInsn(var, increment); + } + } + + public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]) + { + buf.setLength(0); + buf.append(tab2).append("TABLESWITCH\n"); + for (int i = 0; i < labels.length; ++i) { + buf.append(tab3).append(min + i).append(": "); + appendLabel(labels[i]); + buf.append('\n'); + } + buf.append(tab3).append("default: "); + appendLabel(dflt); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + } + + public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]) + { + buf.setLength(0); + buf.append(tab2).append("LOOKUPSWITCH\n"); + for (int i = 0; i < labels.length; ++i) { + buf.append(tab3).append(keys[i]).append(": "); + appendLabel(labels[i]); + buf.append('\n'); + } + buf.append(tab3).append("default: "); + appendLabel(dflt); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + } + + public void visitMultiANewArrayInsn(final String desc, final int dims) { + buf.setLength(0); + buf.append(tab2).append("MULTIANEWARRAY "); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append(' ').append(dims).append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitMultiANewArrayInsn(desc, dims); + } + } + + public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type) + { + buf.setLength(0); + buf.append(tab2).append("TRYCATCHBLOCK "); + appendLabel(start); + buf.append(' '); + appendLabel(end); + buf.append(' '); + appendLabel(handler); + buf.append(' '); + appendDescriptor(INTERNAL_NAME, type); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitTryCatchBlock(start, end, handler, type); + } + } + + public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index) + { + buf.setLength(0); + buf.append(tab2).append("LOCALVARIABLE ").append(name).append(' '); + appendDescriptor(FIELD_DESCRIPTOR, desc); + buf.append(' '); + appendLabel(start); + buf.append(' '); + appendLabel(end); + buf.append(' ').append(index).append('\n'); + + if (signature != null) { + buf.append(tab2); + appendDescriptor(FIELD_SIGNATURE, signature); + + TraceSignatureVisitor sv = new TraceSignatureVisitor(0); + SignatureReader r = new SignatureReader(signature); + r.acceptType(sv); + buf.append(tab2) + .append("// declaration: ") + .append(sv.getDeclaration()) + .append('\n'); + } + text.add(buf.toString()); + + if (mv != null) { + mv.visitLocalVariable(name, desc, signature, start, end, index); + } + } + + public void visitLineNumber(final int line, final Label start) { + buf.setLength(0); + buf.append(tab2).append("LINENUMBER ").append(line).append(' '); + appendLabel(start); + buf.append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitLineNumber(line, start); + } + } + + public void visitMaxs(final int maxStack, final int maxLocals) { + buf.setLength(0); + buf.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n'); + text.add(buf.toString()); + + buf.setLength(0); + buf.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n'); + text.add(buf.toString()); + + if (mv != null) { + mv.visitMaxs(maxStack, maxLocals); + } + } + + public void visitEnd() { + super.visitEnd(); + + if (mv != null) { + mv.visitEnd(); + } + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + /** + * 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. + */ + public void appendLabel(final Label l) { + String name = (String) labelNames.get(l); + if (name == null) { + name = "L" + labelNames.size(); + labelNames.put(l, name); + } + buf.append(name); + } +}
diff --git a/asmx/src/org/objectweb/asm/util/TraceSignatureVisitor.java b/asmx/src/org/objectweb/asm/util/TraceSignatureVisitor.java new file mode 100644 index 0000000..c7145c0 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/TraceSignatureVisitor.java
@@ -0,0 +1,300 @@ +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.signature.SignatureVisitor; + +/** + * A {@link SignatureVisitor} that prints a disassembled view of the signature + * it visits. + * + * @author Eugene Kuleshov + * @author Eric Bruneton + */ +public class TraceSignatureVisitor implements SignatureVisitor { + + private StringBuffer declaration; + + private boolean isInterface; + + private boolean seenFormalParameter; + + private boolean seenInterfaceBound; + + private boolean seenParameter; + + private boolean seenInterface; + + private StringBuffer returnType; + + private StringBuffer exceptions; + + /** + * Stack used to keep track of class types that have arguments. Each element + * of this stack is a boolean encoded in one bit. The top of the stack is + * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping = + * /2. + */ + private int argumentStack; + + /** + * Stack used to keep track of array class types. Each element of this stack + * is a boolean encoded in one bit. The top of the stack is the lowest order + * bit. Pushing false = *2, pushing true = *2+1, popping = /2. + */ + private int arrayStack; + + private String separator = ""; + + public TraceSignatureVisitor(int access) { + isInterface = (access & Opcodes.ACC_INTERFACE) != 0; + this.declaration = new StringBuffer(); + } + + private TraceSignatureVisitor(StringBuffer buf) { + this.declaration = buf; + } + + public void visitFormalTypeParameter(String name) { + declaration.append(seenFormalParameter ? ", " : "<").append(name); + seenFormalParameter = true; + seenInterfaceBound = false; + } + + public SignatureVisitor visitClassBound() { + separator = " extends "; + startType(); + return this; + } + + public SignatureVisitor visitInterfaceBound() { + separator = seenInterfaceBound ? ", " : " extends "; + seenInterfaceBound = true; + startType(); + return this; + } + + public SignatureVisitor visitSuperclass() { + endFormals(); + separator = " extends "; + startType(); + return this; + } + + public SignatureVisitor visitInterface() { + separator = seenInterface ? ", " : (isInterface + ? " extends " + : " implements "); + seenInterface = true; + startType(); + return this; + } + + public SignatureVisitor visitParameterType() { + endFormals(); + if (!seenParameter) { + seenParameter = true; + declaration.append('('); + } else { + declaration.append(", "); + } + startType(); + return this; + } + + public SignatureVisitor visitReturnType() { + endFormals(); + if (!seenParameter) { + declaration.append('('); + } else { + seenParameter = false; + } + declaration.append(')'); + returnType = new StringBuffer(); + return new TraceSignatureVisitor(returnType); + } + + public SignatureVisitor visitExceptionType() { + if (exceptions == null) { + exceptions = new StringBuffer(); + } else { + exceptions.append(", "); + } + // startType(); + return new TraceSignatureVisitor(exceptions); + } + + public void visitBaseType(char descriptor) { + switch (descriptor) { + case 'V': + declaration.append("void"); + break; + case 'B': + declaration.append("byte"); + break; + case 'J': + declaration.append("long"); + break; + case 'Z': + declaration.append("boolean"); + break; + case 'I': + declaration.append("int"); + break; + case 'S': + declaration.append("short"); + break; + case 'C': + declaration.append("char"); + break; + case 'F': + declaration.append("float"); + break; + // case 'D': + default: + declaration.append("double"); + break; + } + endType(); + } + + public void visitTypeVariable(String name) { + declaration.append(name); + endType(); + } + + public SignatureVisitor visitArrayType() { + startType(); + arrayStack |= 1; + return this; + } + + public void visitClassType(String name) { + if (!"java/lang/Object".equals(name)) { + declaration.append(separator).append(name.replace('/', '.')); + } else { + // Map<java.lang.Object,java.util.List> + // or + // abstract public V get(Object key); (seen in Dictionary.class) + // should have Object + // but java.lang.String extends java.lang.Object is unnecessary + boolean needObjectClass = argumentStack % 2 == 1 || seenParameter; + if (needObjectClass) { + declaration.append(separator).append(name.replace('/', '.')); + } + } + separator = ""; + argumentStack *= 2; + } + + public void visitInnerClassType(String name) { + if (argumentStack % 2 == 1) { + declaration.append('>'); + } + argumentStack /= 2; + declaration.append('.'); + declaration.append(separator).append(name.replace('/', '.')); + separator = ""; + argumentStack *= 2; + } + + public void visitTypeArgument() { + if (argumentStack % 2 == 0) { + ++argumentStack; + declaration.append('<'); + } else { + declaration.append(", "); + } + declaration.append('?'); + } + + public SignatureVisitor visitTypeArgument(char tag) { + if (argumentStack % 2 == 0) { + ++argumentStack; + declaration.append('<'); + } else { + declaration.append(", "); + } + + if (tag == SignatureVisitor.EXTENDS) { + declaration.append("? extends "); + } else if (tag == SignatureVisitor.SUPER) { + declaration.append("? super "); + } + + startType(); + return this; + } + + public void visitEnd() { + if (argumentStack % 2 == 1) { + declaration.append('>'); + } + argumentStack /= 2; + endType(); + } + + public String getDeclaration() { + return declaration.toString(); + } + + public String getReturnType() { + return returnType == null ? null : returnType.toString(); + } + + public String getExceptions() { + return exceptions == null ? null : exceptions.toString(); + } + + // ----------------------------------------------- + + private void endFormals() { + if (seenFormalParameter) { + declaration.append('>'); + seenFormalParameter = false; + } + } + + private void startType() { + arrayStack *= 2; + } + + private void endType() { + if (arrayStack % 2 == 1) { + while (arrayStack % 2 == 1) { + arrayStack /= 2; + declaration.append("[]"); + } + } else { + arrayStack /= 2; + } + } +}
diff --git a/asmx/src/org/objectweb/asm/util/TraceTypeAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/TraceTypeAnnotationVisitor.java new file mode 100644 index 0000000..8ae0069 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/TraceTypeAnnotationVisitor.java
@@ -0,0 +1,287 @@ +package org.objectweb.asm.util; + +import org.objectweb.asm.TypeAnnotationVisitor; + +import com.sun.tools.javac.code.TargetType; +import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry; + +/** + * An {@link TypeAnnotationVisitor} that prints a disassembled view of the + * extended annotations it visits. + * + * @author jaimeq + */ + +public class TraceTypeAnnotationVisitor extends TraceAnnotationVisitor + implements TypeAnnotationVisitor +{ + + /** + * The {@link TypeAnnotationVisitor} to which this visitor + * delegates calls. May be <tt>null</tt>. + */ + protected TypeAnnotationVisitor xav; + + /** + * A string representing two consecutive tabs, as defined in + * {@link TraceAbstractVisitor}. + */ + protected String doubleTab = tab + tab; + + // variables necessary in order to print reference info for + // extended annotation + private int xtarget_type; + private int xoffset; + private int xlocation_length; + private TypePathEntry xlocations[]; + private int xlocations_index; + private int xstart_pc; + private int xlength; + private int xindex; + private int xparam_index; + private int xbound_index; + private int xexception_index; + private int xtype_index; + + /** + * Constructs a new {@link TraceTypeAnnotationVisitor}. + */ + public TraceTypeAnnotationVisitor() { + // ignore + } + + public void visitEnd() { + super.visitEnd(); + finishExtendedPart(); + } + + // ------------------------------------------------------------------------ + // Utility methods + // ------------------------------------------------------------------------ + + protected TraceTypeAnnotationVisitor createTraceTypeAnnotationVisitor() { + return new TraceTypeAnnotationVisitor(); + } + + /** + * Prints the reference info of this extended annotation to this.text + */ + private void finishExtendedPart() { + buf.setLength(0); + buf.append("\n extended annotation: \n"); + buf.append(doubleTab).append("target_type: ") + .append(xtarget_type).append("\n"); + + TargetType tt = TargetType.fromTargetTypeValue(xtarget_type); + switch(tt) { + // type test (instanceof) + // object creation + // constructor/method reference receiver + // { + // u2 offset; + // } reference_info; + case INSTANCEOF: + case NEW: + case CONSTRUCTOR_REFERENCE: + case METHOD_REFERENCE: + buf.append(doubleTab).append("offset: ").append(xoffset).append("\n"); + break; + + // method receiver + // { + // } reference_info; + case METHOD_RECEIVER: + break; + + // local variable + // { + // u2 start_pc; + // u2 length; + // u2 index; + // } reference_info; + case LOCAL_VARIABLE: + // resource variable + case RESOURCE_VARIABLE: + buf.append(doubleTab).append("start_pc: ").append(xstart_pc).append("\n"); + buf.append(doubleTab).append("length: ").append(xlength).append("\n"); + buf.append(doubleTab).append("index: ").append(xindex).append("\n"); + break; + + // method return type + // { + // } reference_info; + case METHOD_RETURN: + break; + + // method parameter + // { + // TEMP this should contain the index but doesn't, so for the moment + // we don't print an index + // } reference_info; + case METHOD_FORMAL_PARAMETER: + buf.append(doubleTab).append("index: ").append("FIXME").append("\n"); + break; + + // field + // { + // } reference_info; + case FIELD: + break; + + // class type parameter bound + // method type parameter bound + // { + // u1 bound_index; + // } reference_info; + case CLASS_TYPE_PARAMETER_BOUND: + case METHOD_TYPE_PARAMETER_BOUND: + buf.append(doubleTab).append("param_index: ").append(xparam_index).append("\n"); + buf.append(doubleTab).append("bound_index: ").append(xbound_index).append("\n"); + break; + + // class extends/implements + // exception type in throws/implements + // { + // u1 type_index; + // } + case CLASS_EXTENDS: + case THROWS: + buf.append(doubleTab).append("type_index: ").append(xtype_index).append("\n"); + break; + + // typecast + // type argument in constructor call + // type argument in method call + // type argument in constructor reference + // type argument in method reference + // { + // u2 offset; + // u1 type_index; + // } reference_info; + case CAST: + case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: + case METHOD_INVOCATION_TYPE_ARGUMENT: + case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: + case METHOD_REFERENCE_TYPE_ARGUMENT: + buf.append(doubleTab).append("offset: ").append(xoffset).append("\n"); + buf.append(doubleTab).append("type_index: ").append(xtype_index).append("\n"); + break; + + // method type parameter + // { + // u1 param_index; + // } reference_info; + case METHOD_TYPE_PARAMETER: + buf.append(doubleTab).append("param_index: ").append(xparam_index).append("\n"); + break; + + default: throw new RuntimeException("Unrecognized target type: + " + xtarget_type); + } + + // now print out location string for generic target types + if (xlocation_length != 0) { + buf.append(doubleTab).append("location_length: " + xlocation_length).append("\n"); + buf.append(doubleTab).append("locations: "); + boolean first = true; + for(int i = 0; i < xlocations.length ; i++) { + if(!first) { + buf.append(", "); + } + first = false; + buf.append(xlocations[i]); + } + buf.append("\n"); + } + + text.add(buf.toString()); + } + + public void visitXTargetType(int target_type) { + this.xtarget_type = target_type; + if(xav != null) { + xav.visitXTargetType(target_type); + } + } + + public void visitXOffset(int offset) { + this.xoffset = offset; + if(xav != null) { + xav.visitXOffset(offset); + } + } + + public void visitXLocationLength(int location_length) { + this.xlocation_length = location_length; + this.xlocations = new TypePathEntry[this.xlocation_length]; + this.xlocations_index = 0; + if(xav != null) { + xav.visitXLocationLength(location_length); + } + } + + public void visitXLocation(TypePathEntry location) { + this.xlocations[xlocations_index] = location; + this.xlocations_index++; + if(xav != null) { + xav.visitXLocation(location); + } + } + + public void visitXNumEntries(int num_entries) { + if(xav != null) { + xav.visitXNumEntries(num_entries); + } + } + + public void visitXStartPc(int start_pc) { + this.xstart_pc = start_pc; + if(xav != null) { + xav.visitXStartPc(start_pc); + } + } + + public void visitXLength(int length) { + this.xlength = length; + if(xav != null) { + xav.visitXLength(length); + } + } + + public void visitXIndex(int index) { + this.xindex = index; + if(xav != null) { + xav.visitXIndex(index); + } + } + + public void visitXParamIndex(int param_index) { + this.xparam_index = param_index; + if(xav != null) { + xav.visitXParamIndex(param_index); + } + } + + public void visitXBoundIndex(int bound_index) { + this.xbound_index = bound_index; + if(xav != null) { + xav.visitXBoundIndex(bound_index); + } + } + + public void visitXTypeIndex(int type_index) { + this.xtype_index = type_index; + if(xav != null) { + xav.visitXTypeIndex(type_index); + } + } + + public void visitXExceptionIndex(int exception_index) { + this.xexception_index = exception_index; + if(xav != null) { + xav.visitXExceptionIndex(exception_index); + } + } + + public void visitXNameAndArgsSize() { + } +}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapAttribute.java b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapAttribute.java new file mode 100644 index 0000000..88c7af8 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapAttribute.java
@@ -0,0 +1,223 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util.attrs; + +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Label; +import org.objectweb.asm.attrs.StackMapAttribute; +import org.objectweb.asm.attrs.StackMapFrame; +import org.objectweb.asm.attrs.StackMapType; + +/** + * An {@link ASMifiable} {@link StackMapAttribute} sub class. + * + * @author Eugene Kuleshov + */ +public class ASMStackMapAttribute extends StackMapAttribute implements + ASMifiable, + Traceable +{ + /** + * Length of the attribute used for comparison + */ + private int len; + + public ASMStackMapAttribute() { + super(); + } + + public ASMStackMapAttribute(List frames, int len) { + super(frames); + this.len = len; + } + + protected Attribute read( + ClassReader cr, + int off, + int len, + char[] buf, + int codeOff, + Label[] labels) + { + StackMapAttribute attr = (StackMapAttribute) super.read(cr, + off, + len, + buf, + codeOff, + labels); + + return new ASMStackMapAttribute(attr.getFrames(), len); + } + + public void asmify(StringBuffer buf, String varName, Map labelNames) { + List frames = getFrames(); + buf.append("{\n"); + buf.append("StackMapAttribute ").append(varName).append("Attr"); + buf.append(" = new StackMapAttribute();\n"); + if (frames.size() > 0) { + for (int i = 0; i < frames.size(); i++) { + asmify((StackMapFrame) frames.get(i), buf, varName + "frame" + + i, labelNames); + } + } + buf.append(varName).append(".visitAttribute(").append(varName); + buf.append("Attr);\n}\n"); + } + + void asmify( + StackMapFrame f, + StringBuffer buf, + String varName, + Map labelNames) + { + declareLabel(buf, labelNames, f.label); + buf.append("{\n"); + + buf.append("StackMapFrame ") + .append(varName) + .append(" = new StackMapFrame();\n"); + + buf.append(varName) + .append(".label = ") + .append(labelNames.get(f.label)) + .append(";\n"); + + asmifyTypeInfo(buf, varName, labelNames, f.locals, "locals"); + asmifyTypeInfo(buf, varName, labelNames, f.stack, "stack"); + + buf.append("cvAttr.frames.add(").append(varName).append(");\n"); + buf.append("}\n"); + } + + void asmifyTypeInfo( + StringBuffer buf, + String varName, + Map labelNames, + List infos, + String field) + { + if (infos.size() > 0) { + buf.append("{\n"); + for (int i = 0; i < infos.size(); i++) { + StackMapType typeInfo = (StackMapType) infos.get(i); + String localName = varName + "Info" + i; + int type = typeInfo.getType(); + buf.append("StackMapType ") + .append(localName) + .append(" = StackMapType.getTypeInfo( StackMapType.ITEM_") + .append(StackMapType.ITEM_NAMES[type]) + .append(");\n"); + + switch (type) { + case StackMapType.ITEM_Object: // + buf.append(localName) + .append(".setObject(\"") + .append(typeInfo.getObject()) + .append("\");\n"); + break; + + case StackMapType.ITEM_Uninitialized: // + declareLabel(buf, labelNames, typeInfo.getLabel()); + buf.append(localName) + .append(".setLabel(") + .append(labelNames.get(typeInfo.getLabel())) + .append(");\n"); + break; + } + buf.append(varName) + .append(".") + .append(field) + .append(".add(") + .append(localName) + .append(");\n"); + } + buf.append("}\n"); + } + } + + static void declareLabel(StringBuffer buf, Map labelNames, Label l) { + String name = (String) labelNames.get(l); + if (name == null) { + name = "l" + labelNames.size(); + labelNames.put(l, name); + buf.append("Label ").append(name).append(" = new Label();\n"); + } + } + + public void trace(StringBuffer buf, Map labelNames) { + List frames = getFrames(); + buf.append("[\n"); + for (int i = 0; i < frames.size(); i++) { + StackMapFrame f = (StackMapFrame) frames.get(i); + + buf.append(" Frame:"); + appendLabel(buf, labelNames, f.label); + + buf.append(" locals["); + traceTypeInfo(buf, labelNames, f.locals); + buf.append("]"); + buf.append(" stack["); + traceTypeInfo(buf, labelNames, f.stack); + buf.append("]\n"); + } + buf.append(" ] length:").append(len).append("\n"); + } + + private void traceTypeInfo(StringBuffer buf, Map labelNames, List infos) { + String sep = ""; + for (int i = 0; i < infos.size(); i++) { + StackMapType t = (StackMapType) infos.get(i); + + buf.append(sep).append(StackMapType.ITEM_NAMES[t.getType()]); + sep = ", "; + if (t.getType() == StackMapType.ITEM_Object) { + buf.append(":").append(t.getObject()); + } + if (t.getType() == StackMapType.ITEM_Uninitialized) { + buf.append(":"); + appendLabel(buf, labelNames, t.getLabel()); + } + } + } + + protected void appendLabel(StringBuffer buf, Map labelNames, Label l) { + String name = (String) labelNames.get(l); + if (name == null) { + name = "L" + labelNames.size(); + labelNames.put(l, name); + } + buf.append(name); + } + +}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapTableAttribute.java b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapTableAttribute.java new file mode 100644 index 0000000..b878357 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapTableAttribute.java
@@ -0,0 +1,214 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util.attrs; + +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Label; +import org.objectweb.asm.attrs.StackMapTableAttribute; +import org.objectweb.asm.attrs.StackMapFrame; +import org.objectweb.asm.attrs.StackMapType; + +/** + * An {@link ASMifiable} {@link StackMapTableAttribute} sub class. + * + * @author Eugene Kuleshov + */ +public class ASMStackMapTableAttribute extends StackMapTableAttribute implements + ASMifiable, + Traceable +{ + /** + * Length of the attribute used for comparison + */ + private int len; + + public ASMStackMapTableAttribute() { + super(); + } + + public ASMStackMapTableAttribute(List frames, int len) { + super(frames); + this.len = len; + } + + protected Attribute read( + ClassReader cr, + int off, + int len, + char[] buf, + int codeOff, + Label[] labels) + { + StackMapTableAttribute attr = (StackMapTableAttribute) super.read(cr, + off, + len, + buf, + codeOff, + labels); + + return new ASMStackMapTableAttribute(attr.getFrames(), len); + } + + public void asmify(StringBuffer buf, String varName, Map labelNames) { + List frames = getFrames(); + if (frames.size() == 0) { + buf.append("List frames = Collections.EMPTY_LIST;\n"); + } else { + buf.append("List frames = new ArrayList();\n"); + for (int i = 0; i < frames.size(); i++) { + buf.append("{\n"); + StackMapFrame f = (StackMapFrame) frames.get(i); + declareLabel(buf, labelNames, f.label); + + String frameVar = varName + "frame" + i; + asmifyTypeInfo(buf, frameVar, labelNames, f.locals, "locals"); + asmifyTypeInfo(buf, frameVar, labelNames, f.stack, "stack"); + + buf.append("StackMapFrame ") + .append(frameVar) + .append(" = new StackMapFrame(") + .append(labelNames.get(f.label)) + .append(", locals, stack);\n"); + buf.append("frames.add(").append(frameVar).append(");\n"); + buf.append("}\n"); + } + } + buf.append("StackMapTableAttribute ").append(varName); + buf.append(" = new StackMapTableAttribute(frames);\n"); + } + + void asmifyTypeInfo( + StringBuffer buf, + String varName, + Map labelNames, + List infos, + String field) + { + if (infos.size() == 0) { + buf.append("List ") + .append(field) + .append(" = Collections.EMPTY_LIST;\n"); + } else { + buf.append("List ").append(field).append(" = new ArrayList();\n"); + buf.append("{\n"); + for (int i = 0; i < infos.size(); i++) { + StackMapType typeInfo = (StackMapType) infos.get(i); + String localName = varName + "Info" + i; + int type = typeInfo.getType(); + buf.append("StackMapType ") + .append(localName) + .append(" = StackMapType.getTypeInfo( StackMapType.ITEM_") + .append(StackMapType.ITEM_NAMES[type]) + .append(");\n"); + + switch (type) { + case StackMapType.ITEM_Object: // + buf.append(localName) + .append(".setObject(\"") + .append(typeInfo.getObject()) + .append("\");\n"); + break; + + case StackMapType.ITEM_Uninitialized: // + declareLabel(buf, labelNames, typeInfo.getLabel()); + buf.append(localName) + .append(".setLabel(") + .append(labelNames.get(typeInfo.getLabel())) + .append(");\n"); + break; + } + buf.append(field) + .append(".add(") + .append(localName) + .append(");\n"); + } + buf.append("}\n"); + } + } + + static void declareLabel(StringBuffer buf, Map labelNames, Label l) { + String name = (String) labelNames.get(l); + if (name == null) { + name = "l" + labelNames.size(); + labelNames.put(l, name); + buf.append("Label ").append(name).append(" = new Label();\n"); + } + } + + public void trace(StringBuffer buf, Map labelNames) { + List frames = getFrames(); + buf.append("[\n"); + for (int i = 0; i < frames.size(); i++) { + StackMapFrame f = (StackMapFrame) frames.get(i); + + buf.append(" Frame:"); + appendLabel(buf, labelNames, f.label); + + buf.append(" locals["); + traceTypeInfo(buf, labelNames, f.locals); + buf.append("]"); + buf.append(" stack["); + traceTypeInfo(buf, labelNames, f.stack); + buf.append("]\n"); + } + buf.append(" ] length:").append(len).append("\n"); + } + + private void traceTypeInfo(StringBuffer buf, Map labelNames, List infos) { + String sep = ""; + for (int i = 0; i < infos.size(); i++) { + StackMapType t = (StackMapType) infos.get(i); + + buf.append(sep).append(StackMapType.ITEM_NAMES[t.getType()]); + sep = ", "; + if (t.getType() == StackMapType.ITEM_Object) { + buf.append(":").append(t.getObject()); + } + if (t.getType() == StackMapType.ITEM_Uninitialized) { + buf.append(":"); + appendLabel(buf, labelNames, t.getLabel()); + } + } + } + + protected void appendLabel(StringBuffer buf, Map labelNames, Label l) { + String name = (String) labelNames.get(l); + if (name == null) { + name = "L" + labelNames.size(); + labelNames.put(l, name); + } + buf.append(name); + } + +}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/ASMifiable.java b/asmx/src/org/objectweb/asm/util/attrs/ASMifiable.java new file mode 100644 index 0000000..149d7da --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/attrs/ASMifiable.java
@@ -0,0 +1,53 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util.attrs; + +import java.util.Map; + +/** + * An attribute that can print the ASM code to create an equivalent attribute. + * + * Implementation should print the ASM code that generates attribute data + * structures for current attribute state. + * + * @author Eugene Kuleshov + */ +public interface ASMifiable { + + /** + * Prints the ASM code to create an attribute equal to this attribute. + * + * @param buf A buffer used for printing Java code. + * @param varName name of the variable in a printed code used to store + * attribute instance. + * @param labelNames map of label instances to their names. + */ + void asmify(StringBuffer buf, String varName, Map labelNames); +}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/Traceable.java b/asmx/src/org/objectweb/asm/util/attrs/Traceable.java new file mode 100644 index 0000000..c40d2a6 --- /dev/null +++ b/asmx/src/org/objectweb/asm/util/attrs/Traceable.java
@@ -0,0 +1,52 @@ +/** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.util.attrs; + +import java.util.Map; + +/** + * An attribute that can print eadable representation of the attribute. + * + * Implementation should construct readable output from an attribute data + * structures for current attribute state. Such representation could be used in + * unit test assertions. + * + * @author Eugene Kuleshov + */ +public interface Traceable { + + /** + * Build a human readable representation of the attribute. + * + * @param buf A buffer used for printing Java code. + * @param labelNames map of label instances to their names. + */ + void trace(StringBuffer buf, Map labelNames); +}
diff --git a/asmx/src/org/objectweb/asm/xml/ASMContentHandler.java b/asmx/src/org/objectweb/asm/xml/ASMContentHandler.java new file mode 100644 index 0000000..1f6fee0 --- /dev/null +++ b/asmx/src/org/objectweb/asm/xml/ASMContentHandler.java
@@ -0,0 +1,1215 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004, 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 org.objectweb.asm.xml; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * 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 org.objectweb.asm.xml.SAXClassAdapter + * @see org.objectweb.asm.xml.Processor + * + * @author Eugene Kuleshov + */ +public class ASMContentHandler extends DefaultHandler implements Opcodes { + /** + * Stack of the intermediate processing contexts. + */ + private List stack = new ArrayList(); + + /** + * Complete name of the current element. + */ + private String match = ""; + + /** + * <tt>true</tt> if the maximum stack size and number of local variables + * must be automatically computed. + */ + protected boolean computeMax; + + /** + * Output stream to write result bytecode. + */ + protected OutputStream os; + + /** + * Current instance of the {@link ClassWriter ClassWriter} used to write + * class bytecode. + */ + protected ClassWriter cw; + + /** + * 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/annotationDefault", + new AnnotationDefaultRule()); + + RULES.add(BASE + "/method/code/*", new OpcodesRule()); // opcodes + + 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/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("*/parameterAnnotation", new AnnotationParameterRule()); + 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 Map OPCODES = new HashMap(); + static { + OPCODES.put("NOP", new Opcode(NOP, OpcodeGroup.INSN)); + OPCODES.put("ACONST_NULL", new Opcode(ACONST_NULL, OpcodeGroup.INSN)); + OPCODES.put("ICONST_M1", new Opcode(ICONST_M1, OpcodeGroup.INSN)); + OPCODES.put("ICONST_0", new Opcode(ICONST_0, OpcodeGroup.INSN)); + OPCODES.put("ICONST_1", new Opcode(ICONST_1, OpcodeGroup.INSN)); + OPCODES.put("ICONST_2", new Opcode(ICONST_2, OpcodeGroup.INSN)); + OPCODES.put("ICONST_3", new Opcode(ICONST_3, OpcodeGroup.INSN)); + OPCODES.put("ICONST_4", new Opcode(ICONST_4, OpcodeGroup.INSN)); + OPCODES.put("ICONST_5", new Opcode(ICONST_5, OpcodeGroup.INSN)); + OPCODES.put("LCONST_0", new Opcode(LCONST_0, OpcodeGroup.INSN)); + OPCODES.put("LCONST_1", new Opcode(LCONST_1, OpcodeGroup.INSN)); + OPCODES.put("FCONST_0", new Opcode(FCONST_0, OpcodeGroup.INSN)); + OPCODES.put("FCONST_1", new Opcode(FCONST_1, OpcodeGroup.INSN)); + OPCODES.put("FCONST_2", new Opcode(FCONST_2, OpcodeGroup.INSN)); + OPCODES.put("DCONST_0", new Opcode(DCONST_0, OpcodeGroup.INSN)); + OPCODES.put("DCONST_1", new Opcode(DCONST_1, OpcodeGroup.INSN)); + OPCODES.put("BIPUSH", new Opcode(BIPUSH, OpcodeGroup.INSN_INT)); + OPCODES.put("SIPUSH", new Opcode(SIPUSH, OpcodeGroup.INSN_INT)); + OPCODES.put("LDC", new Opcode(LDC, OpcodeGroup.INSN_LDC)); + OPCODES.put("ILOAD", new Opcode(ILOAD, OpcodeGroup.INSN_VAR)); + OPCODES.put("LLOAD", new Opcode(LLOAD, OpcodeGroup.INSN_VAR)); + OPCODES.put("FLOAD", new Opcode(FLOAD, OpcodeGroup.INSN_VAR)); + OPCODES.put("DLOAD", new Opcode(DLOAD, OpcodeGroup.INSN_VAR)); + OPCODES.put("ALOAD", new Opcode(ALOAD, OpcodeGroup.INSN_VAR)); + OPCODES.put("IALOAD", new Opcode(IALOAD, OpcodeGroup.INSN)); + OPCODES.put("LALOAD", new Opcode(LALOAD, OpcodeGroup.INSN)); + OPCODES.put("FALOAD", new Opcode(FALOAD, OpcodeGroup.INSN)); + OPCODES.put("DALOAD", new Opcode(DALOAD, OpcodeGroup.INSN)); + OPCODES.put("AALOAD", new Opcode(AALOAD, OpcodeGroup.INSN)); + OPCODES.put("BALOAD", new Opcode(BALOAD, OpcodeGroup.INSN)); + OPCODES.put("CALOAD", new Opcode(CALOAD, OpcodeGroup.INSN)); + OPCODES.put("SALOAD", new Opcode(SALOAD, OpcodeGroup.INSN)); + OPCODES.put("ISTORE", new Opcode(ISTORE, OpcodeGroup.INSN_VAR)); + OPCODES.put("LSTORE", new Opcode(LSTORE, OpcodeGroup.INSN_VAR)); + OPCODES.put("FSTORE", new Opcode(FSTORE, OpcodeGroup.INSN_VAR)); + OPCODES.put("DSTORE", new Opcode(DSTORE, OpcodeGroup.INSN_VAR)); + OPCODES.put("ASTORE", new Opcode(ASTORE, OpcodeGroup.INSN_VAR)); + OPCODES.put("IASTORE", new Opcode(IASTORE, OpcodeGroup.INSN)); + OPCODES.put("LASTORE", new Opcode(LASTORE, OpcodeGroup.INSN)); + OPCODES.put("FASTORE", new Opcode(FASTORE, OpcodeGroup.INSN)); + OPCODES.put("DASTORE", new Opcode(DASTORE, OpcodeGroup.INSN)); + OPCODES.put("AASTORE", new Opcode(AASTORE, OpcodeGroup.INSN)); + OPCODES.put("BASTORE", new Opcode(BASTORE, OpcodeGroup.INSN)); + OPCODES.put("CASTORE", new Opcode(CASTORE, OpcodeGroup.INSN)); + OPCODES.put("SASTORE", new Opcode(SASTORE, OpcodeGroup.INSN)); + OPCODES.put("POP", new Opcode(POP, OpcodeGroup.INSN)); + OPCODES.put("POP2", new Opcode(POP2, OpcodeGroup.INSN)); + OPCODES.put("DUP", new Opcode(DUP, OpcodeGroup.INSN)); + OPCODES.put("DUP_X1", new Opcode(DUP_X1, OpcodeGroup.INSN)); + OPCODES.put("DUP_X2", new Opcode(DUP_X2, OpcodeGroup.INSN)); + OPCODES.put("DUP2", new Opcode(DUP2, OpcodeGroup.INSN)); + OPCODES.put("DUP2_X1", new Opcode(DUP2_X1, OpcodeGroup.INSN)); + OPCODES.put("DUP2_X2", new Opcode(DUP2_X2, OpcodeGroup.INSN)); + OPCODES.put("SWAP", new Opcode(SWAP, OpcodeGroup.INSN)); + OPCODES.put("IADD", new Opcode(IADD, OpcodeGroup.INSN)); + OPCODES.put("LADD", new Opcode(LADD, OpcodeGroup.INSN)); + OPCODES.put("FADD", new Opcode(FADD, OpcodeGroup.INSN)); + OPCODES.put("DADD", new Opcode(DADD, OpcodeGroup.INSN)); + OPCODES.put("ISUB", new Opcode(ISUB, OpcodeGroup.INSN)); + OPCODES.put("LSUB", new Opcode(LSUB, OpcodeGroup.INSN)); + OPCODES.put("FSUB", new Opcode(FSUB, OpcodeGroup.INSN)); + OPCODES.put("DSUB", new Opcode(DSUB, OpcodeGroup.INSN)); + OPCODES.put("IMUL", new Opcode(IMUL, OpcodeGroup.INSN)); + OPCODES.put("LMUL", new Opcode(LMUL, OpcodeGroup.INSN)); + OPCODES.put("FMUL", new Opcode(FMUL, OpcodeGroup.INSN)); + OPCODES.put("DMUL", new Opcode(DMUL, OpcodeGroup.INSN)); + OPCODES.put("IDIV", new Opcode(IDIV, OpcodeGroup.INSN)); + OPCODES.put("LDIV", new Opcode(LDIV, OpcodeGroup.INSN)); + OPCODES.put("FDIV", new Opcode(FDIV, OpcodeGroup.INSN)); + OPCODES.put("DDIV", new Opcode(DDIV, OpcodeGroup.INSN)); + OPCODES.put("IREM", new Opcode(IREM, OpcodeGroup.INSN)); + OPCODES.put("LREM", new Opcode(LREM, OpcodeGroup.INSN)); + OPCODES.put("FREM", new Opcode(FREM, OpcodeGroup.INSN)); + OPCODES.put("DREM", new Opcode(DREM, OpcodeGroup.INSN)); + OPCODES.put("INEG", new Opcode(INEG, OpcodeGroup.INSN)); + OPCODES.put("LNEG", new Opcode(LNEG, OpcodeGroup.INSN)); + OPCODES.put("FNEG", new Opcode(FNEG, OpcodeGroup.INSN)); + OPCODES.put("DNEG", new Opcode(DNEG, OpcodeGroup.INSN)); + OPCODES.put("ISHL", new Opcode(ISHL, OpcodeGroup.INSN)); + OPCODES.put("LSHL", new Opcode(LSHL, OpcodeGroup.INSN)); + OPCODES.put("ISHR", new Opcode(ISHR, OpcodeGroup.INSN)); + OPCODES.put("LSHR", new Opcode(LSHR, OpcodeGroup.INSN)); + OPCODES.put("IUSHR", new Opcode(IUSHR, OpcodeGroup.INSN)); + OPCODES.put("LUSHR", new Opcode(LUSHR, OpcodeGroup.INSN)); + OPCODES.put("IAND", new Opcode(IAND, OpcodeGroup.INSN)); + OPCODES.put("LAND", new Opcode(LAND, OpcodeGroup.INSN)); + OPCODES.put("IOR", new Opcode(IOR, OpcodeGroup.INSN)); + OPCODES.put("LOR", new Opcode(LOR, OpcodeGroup.INSN)); + OPCODES.put("IXOR", new Opcode(IXOR, OpcodeGroup.INSN)); + OPCODES.put("LXOR", new Opcode(LXOR, OpcodeGroup.INSN)); + OPCODES.put("IINC", new Opcode(IINC, OpcodeGroup.INSN_IINC)); + OPCODES.put("I2L", new Opcode(I2L, OpcodeGroup.INSN)); + OPCODES.put("I2F", new Opcode(I2F, OpcodeGroup.INSN)); + OPCODES.put("I2D", new Opcode(I2D, OpcodeGroup.INSN)); + OPCODES.put("L2I", new Opcode(L2I, OpcodeGroup.INSN)); + OPCODES.put("L2F", new Opcode(L2F, OpcodeGroup.INSN)); + OPCODES.put("L2D", new Opcode(L2D, OpcodeGroup.INSN)); + OPCODES.put("F2I", new Opcode(F2I, OpcodeGroup.INSN)); + OPCODES.put("F2L", new Opcode(F2L, OpcodeGroup.INSN)); + OPCODES.put("F2D", new Opcode(F2D, OpcodeGroup.INSN)); + OPCODES.put("D2I", new Opcode(D2I, OpcodeGroup.INSN)); + OPCODES.put("D2L", new Opcode(D2L, OpcodeGroup.INSN)); + OPCODES.put("D2F", new Opcode(D2F, OpcodeGroup.INSN)); + OPCODES.put("I2B", new Opcode(I2B, OpcodeGroup.INSN)); + OPCODES.put("I2C", new Opcode(I2C, OpcodeGroup.INSN)); + OPCODES.put("I2S", new Opcode(I2S, OpcodeGroup.INSN)); + OPCODES.put("LCMP", new Opcode(LCMP, OpcodeGroup.INSN)); + OPCODES.put("FCMPL", new Opcode(FCMPL, OpcodeGroup.INSN)); + OPCODES.put("FCMPG", new Opcode(FCMPG, OpcodeGroup.INSN)); + OPCODES.put("DCMPL", new Opcode(DCMPL, OpcodeGroup.INSN)); + OPCODES.put("DCMPG", new Opcode(DCMPG, OpcodeGroup.INSN)); + OPCODES.put("IFEQ", new Opcode(IFEQ, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IFNE", new Opcode(IFNE, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IFLT", new Opcode(IFLT, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IFGE", new Opcode(IFGE, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IFGT", new Opcode(IFGT, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IFLE", new Opcode(IFLE, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ICMPEQ", new Opcode(IF_ICMPEQ, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ICMPNE", new Opcode(IF_ICMPNE, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ICMPLT", new Opcode(IF_ICMPLT, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ICMPGE", new Opcode(IF_ICMPGE, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ICMPGT", new Opcode(IF_ICMPGT, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ICMPLE", new Opcode(IF_ICMPLE, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ACMPEQ", new Opcode(IF_ACMPEQ, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IF_ACMPNE", new Opcode(IF_ACMPNE, OpcodeGroup.INSN_JUMP)); + OPCODES.put("GOTO", new Opcode(GOTO, OpcodeGroup.INSN_JUMP)); + OPCODES.put("JSR", new Opcode(JSR, OpcodeGroup.INSN_JUMP)); + OPCODES.put("RET", new Opcode(RET, OpcodeGroup.INSN_VAR)); + // OPCODES.put( "TABLESWITCH", new Opcode( TABLESWITCH, + // "visiTableSwitchInsn")); + // OPCODES.put( "LOOKUPSWITCH", new Opcode( LOOKUPSWITCH, + // "visitLookupSwitch")); + OPCODES.put("IRETURN", new Opcode(IRETURN, OpcodeGroup.INSN)); + OPCODES.put("LRETURN", new Opcode(LRETURN, OpcodeGroup.INSN)); + OPCODES.put("FRETURN", new Opcode(FRETURN, OpcodeGroup.INSN)); + OPCODES.put("DRETURN", new Opcode(DRETURN, OpcodeGroup.INSN)); + OPCODES.put("ARETURN", new Opcode(ARETURN, OpcodeGroup.INSN)); + OPCODES.put("RETURN", new Opcode(RETURN, OpcodeGroup.INSN)); + OPCODES.put("GETSTATIC", new Opcode(GETSTATIC, OpcodeGroup.INSN_FIELD)); + OPCODES.put("PUTSTATIC", new Opcode(PUTSTATIC, OpcodeGroup.INSN_FIELD)); + OPCODES.put("GETFIELD", new Opcode(GETFIELD, OpcodeGroup.INSN_FIELD)); + OPCODES.put("PUTFIELD", new Opcode(PUTFIELD, OpcodeGroup.INSN_FIELD)); + OPCODES.put("INVOKEVIRTUAL", new Opcode(INVOKEVIRTUAL, + OpcodeGroup.INSN_METHOD)); + OPCODES.put("INVOKESPECIAL", new Opcode(INVOKESPECIAL, + OpcodeGroup.INSN_METHOD)); + OPCODES.put("INVOKESTATIC", new Opcode(INVOKESTATIC, + OpcodeGroup.INSN_METHOD)); + OPCODES.put("INVOKEINTERFACE", new Opcode(INVOKEINTERFACE, + OpcodeGroup.INSN_METHOD)); + OPCODES.put("NEW", new Opcode(NEW, OpcodeGroup.INSN_TYPE)); + OPCODES.put("NEWARRAY", new Opcode(NEWARRAY, OpcodeGroup.INSN_INT)); + OPCODES.put("ANEWARRAY", new Opcode(ANEWARRAY, OpcodeGroup.INSN_TYPE)); + OPCODES.put("ARRAYLENGTH", new Opcode(ARRAYLENGTH, OpcodeGroup.INSN)); + OPCODES.put("ATHROW", new Opcode(ATHROW, OpcodeGroup.INSN)); + OPCODES.put("CHECKCAST", new Opcode(CHECKCAST, OpcodeGroup.INSN_TYPE)); + OPCODES.put("INSTANCEOF", new Opcode(INSTANCEOF, OpcodeGroup.INSN_TYPE)); + OPCODES.put("MONITORENTER", new Opcode(MONITORENTER, OpcodeGroup.INSN)); + OPCODES.put("MONITOREXIT", new Opcode(MONITOREXIT, OpcodeGroup.INSN)); + OPCODES.put("MULTIANEWARRAY", new Opcode(MULTIANEWARRAY, + OpcodeGroup.INSN_MULTIANEWARRAY)); + OPCODES.put("IFNULL", new Opcode(IFNULL, OpcodeGroup.INSN_JUMP)); + OPCODES.put("IFNONNULL", new Opcode(IFNONNULL, OpcodeGroup.INSN_JUMP)); + } + + /** + * Constructs a new {@link ASMContentHandler ASMContentHandler} object. + * + * @param os output stream to write generated class. + * @param computeMax <tt>true</tt> if the maximum stack size and the + * maximum number of local variables must be automatically computed. + * This value is passed to {@link ClassWriter ClassWriter} instance. + */ + public ASMContentHandler(OutputStream os, boolean computeMax) { + this.os = os; + this.computeMax = computeMax; + } + + /** + * Returns the bytecode of the class that was build with underneath class + * writer. + * + * @return the bytecode of the class that was build with underneath class + * writer or null if there are no classwriter created. + */ + public byte[] toByteArray() { + return cw == null ? null : cw.toByteArray(); + } + + /** + * 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 localName - 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 + */ + public final void startElement( + String ns, + String localName, + String qName, + Attributes list) throws SAXException + { + // the actual element name is either in localName or qName, depending + // on whether the parser is namespace aware + String name = localName; + if (name == null || name.length() < 1) { + name = qName; + } + + // Compute the current matching rule + StringBuffer sb = new StringBuffer(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 localName - 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 + */ + public final void endElement(String ns, String localName, String qName) + throws SAXException + { + // the actual element name is either in localName or qName, depending + // on whether the parser is namespace aware + String name = localName; + if (name == null || name.length() < 1) { + name = qName; + } + + // 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 = ""; + } + } + + /** + * Process notification of the end of a document and write generated + * bytecode into output stream. + * + * @exception SAXException if parsing or writing error is to be reported. + */ + public final void endDocument() throws SAXException { + try { + os.write(cw.toByteArray()); + } catch (IOException ex) { + throw new SAXException(ex.toString(), ex); + } + } + + /** + * Return the top object on the stack without removing it. If there are no + * objects on the stack, return <code>null</code>. + * + * @return the top object on the stack without removing it. + */ + final Object peek() { + return stack.size() == 0 ? null : stack.get(stack.size() - 1); + } + + /** + * Return the n'th object down the stack, where 0 is the top element and + * [getCount()-1] is the bottom element. If the specified index is out of + * range, return <code>null</code>. + * + * @param n Index of the desired element, where 0 is the top of the stack, 1 + * is the next element down, and so on. + * @return the n'th object down the stack. + */ + final Object peek(int n) { + return stack.size() < (n + 1) ? null : stack.get(n); + } + + /** + * Pop the top object off of the stack, and return it. If there are no + * objects on the stack, return <code>null</code>. + * + * @return the top object off of the stack. + */ + final Object pop() { + return stack.size() == 0 ? null : stack.remove(stack.size() - 1); + } + + /** + * Push a new object onto the top of the object stack. + * + * @param object The new object + */ + final void push(Object object) { + stack.add(object); + } + + private static final class RuleSet { + private Map rules = new HashMap(); + + private List lpatterns = new ArrayList(); + + private List rpatterns = new ArrayList(); + + public void add(String path, 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(String path) { + if (rules.containsKey(path)) { + return rules.get(path); + } + + int n = path.lastIndexOf('/'); + for (Iterator it = lpatterns.iterator(); it.hasNext();) { + String pattern = (String) it.next(); + if (path.substring(n).endsWith(pattern)) { + return rules.get(pattern); + } + } + + for (Iterator it = rpatterns.iterator(); it.hasNext();) { + String pattern = (String) it.next(); + if (path.startsWith(pattern)) { + return rules.get(pattern); + } + } + + return null; + } + + } + + /** + * Rule + */ + protected abstract class Rule { + + public void begin(String name, Attributes attrs) { + } + + public void end(String name) { + } + + protected final Object getValue(String desc, String val) { + Object value = null; + if (val != null) { + if (desc.equals("Ljava/lang/String;")) { + value = decode(val); + } else if ("Ljava/lang/Integer;".equals(desc) + || "I".equals(desc) || "S".equals(desc) + || "B".equals(desc) || "C".equals(desc) + || desc.equals("Z")) + { + 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/Integer;".equals(desc) + // || desc.equals("I")) + // { + // value = new Integer(val); + // } else if ("Ljava/lang/Character;".equals(desc) + // || desc.equals("C")) + // { + // value = new Character(decode(val).charAt(0)); + // } else if ("Ljava/lang/Short;".equals(desc) || + // desc.equals("S")) + // { + // value = Short.valueOf(val); + // } else if ("Ljava/lang/Byte;".equals(desc) || + // desc.equals("B")) + // { + // value = Byte.valueOf(val); + + } else if ("Ljava/lang/Long;".equals(desc) || desc.equals("J")) + { + value = new Long(val); + } else if ("Ljava/lang/Float;".equals(desc) || desc.equals("F")) + { + value = new Float(val); + } else if ("Ljava/lang/Double;".equals(desc) + || desc.equals("D")) + { + value = new Double(val); + } else if (Type.getDescriptor(Type.class).equals(desc)) { + value = Type.getType(val); + + // } else if ("[I".equals(desc)) { + // value = new int[0]; // TODO + // } else if ("[C".equals(desc)) { + // value = new char[0]; // TODO + // } else if ("[Z".equals(desc)) { + // value = new boolean[0]; // TODO + // } else if ("[S".equals(desc)) { + // value = new short[0]; // TODO + // } else if ("[B".equals(desc)) { + // value = new byte[0]; // TODO + // } else if ("[J".equals(desc)) { + // value = new long[0]; // TODO + // } else if ("[F".equals(desc)) { + // value = new float[0]; // TODO + // } else if ("[D".equals(desc)) { + // value = new double[0]; // TODO + + } else { + throw new RuntimeException("Invalid value:" + val + + " desc:" + desc + " ctx:" + this); + } + } + return value; + } + + private final String decode(String val) { + StringBuffer sb = new StringBuffer(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) { + System.err.println(val + "\n" + ex.toString()); + ex.printStackTrace(); + throw ex; + } + return sb.toString(); + } + + protected final Label getLabel(Object label) { + Label lbl = (Label) 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(String s) { + int access = 0; + if (s.indexOf("public") != -1) + access |= Opcodes.ACC_PUBLIC; + if (s.indexOf("private") != -1) + access |= Opcodes.ACC_PRIVATE; + if (s.indexOf("protected") != -1) + access |= Opcodes.ACC_PROTECTED; + if (s.indexOf("static") != -1) + access |= Opcodes.ACC_STATIC; + if (s.indexOf("final") != -1) + access |= Opcodes.ACC_FINAL; + if (s.indexOf("super") != -1) + access |= Opcodes.ACC_SUPER; + if (s.indexOf("synchronized") != -1) + access |= Opcodes.ACC_SYNCHRONIZED; + if (s.indexOf("volatile") != -1) + access |= Opcodes.ACC_VOLATILE; + if (s.indexOf("bridge") != -1) + access |= Opcodes.ACC_BRIDGE; + if (s.indexOf("varargs") != -1) + access |= Opcodes.ACC_VARARGS; + if (s.indexOf("transient") != -1) + access |= Opcodes.ACC_TRANSIENT; + if (s.indexOf("native") != -1) + access |= Opcodes.ACC_NATIVE; + if (s.indexOf("interface") != -1) + access |= Opcodes.ACC_INTERFACE; + if (s.indexOf("abstract") != -1) + access |= Opcodes.ACC_ABSTRACT; + if (s.indexOf("strict") != -1) + access |= Opcodes.ACC_STRICT; + if (s.indexOf("synthetic") != -1) + access |= Opcodes.ACC_SYNTHETIC; + if (s.indexOf("annotation") != -1) + access |= Opcodes.ACC_ANNOTATION; + if (s.indexOf("enum") != -1) + access |= Opcodes.ACC_ENUM; + if (s.indexOf("deprecated") != -1) + access |= Opcodes.ACC_DEPRECATED; + return access; + } + + } + + /** + * ClassRule + */ + private final class ClassRule extends Rule { + + public final void begin(String name, Attributes attrs) { + int major = Integer.parseInt(attrs.getValue("major")); + int minor = Integer.parseInt(attrs.getValue("minor")); + cw = new ClassWriter(computeMax); + Map vals = new HashMap(); + vals.put("version", new Integer(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(); + } + + } + + private final class SourceRule extends Rule { + + public void begin(String name, Attributes attrs) { + String file = attrs.getValue("file"); + String debug = attrs.getValue("debug"); + cw.visitSource(file, debug); + } + + } + + /** + * InterfaceRule + */ + private final class InterfaceRule extends Rule { + + public final void begin(String name, Attributes attrs) { + ((List) ((Map) peek()).get("interfaces")).add(attrs.getValue("name")); + } + + } + + /** + * InterfacesRule + */ + private final class InterfacesRule extends Rule { + + public final void end(String element) { + Map vals = (Map) 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"); + List infs = (List) vals.get("interfaces"); + String[] interfaces = (String[]) infs.toArray(new String[infs.size()]); + cw.visit(version, access, name, signature, parent, interfaces); + push(cw); + } + + } + + /** + * OuterClassRule + */ + private final class OuterClassRule extends Rule { + + public final void begin(String element, Attributes attrs) { + String owner = attrs.getValue("owner"); + String name = attrs.getValue("name"); + String desc = attrs.getValue("desc"); + cw.visitOuterClass(owner, name, desc); + } + + } + + /** + * InnerClassRule + */ + private final class InnerClassRule extends Rule { + + public final void begin(String element, Attributes attrs) { + int access = getAccess(attrs.getValue("access")); + String name = attrs.getValue("name"); + String outerName = attrs.getValue("outerName"); + String innerName = attrs.getValue("innerName"); + cw.visitInnerClass(name, outerName, innerName, access); + } + + } + + /** + * FieldRule + */ + private final class FieldRule extends Rule { + + public final void begin(String element, Attributes attrs) { + 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(cw.visitField(access, name, desc, signature, value)); + } + + public void end(String name) { + ((FieldVisitor) pop()).visitEnd(); + } + + } + + /** + * MethodRule + */ + private final class MethodRule extends Rule { + + public final void begin(String name, Attributes attrs) { + labels = new HashMap(); + Map 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(); + } + + public final void end(String name) { + ((MethodVisitor) pop()).visitEnd(); + labels = null; + } + + } + + /** + * ExceptionRule + */ + private final class ExceptionRule extends Rule { + + public final void begin(String name, Attributes attrs) { + ((List) ((Map) peek()).get("exceptions")).add(attrs.getValue("name")); + } + + } + + /** + * ExceptionsRule + */ + private final class ExceptionsRule extends Rule { + + public final void end(String element) { + Map vals = (Map) 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"); + List excs = (List) vals.get("exceptions"); + String[] exceptions = (String[]) excs.toArray(new String[excs.size()]); + + push(cw.visitMethod(access, name, desc, signature, exceptions)); + } + + } + + /** + * TableSwitchRule + */ + private class TableSwitchRule extends Rule { + + public final void begin(String name, Attributes attrs) { + Map 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); + } + + public final void end(String name) { + Map vals = (Map) pop(); + int min = Integer.parseInt((String) vals.get("min")); + int max = Integer.parseInt((String) vals.get("max")); + Label dflt = getLabel(vals.get("dflt")); + List lbls = (List) vals.get("labels"); + Label[] labels = (Label[]) lbls.toArray(new Label[lbls.size()]); + getCodeVisitor().visitTableSwitchInsn(min, max, dflt, labels); + } + + } + + /** + * TableSwitchLabelRule + */ + private final class TableSwitchLabelRule extends Rule { + + public final void begin(String name, Attributes attrs) { + ((List) ((Map) peek()).get("labels")).add(getLabel(attrs.getValue("name"))); + } + + } + + /** + * LookupSwitchRule + */ + private final class LookupSwitchRule extends Rule { + + public final void begin(String name, Attributes attrs) { + Map vals = new HashMap(); + vals.put("dflt", attrs.getValue("dflt")); + vals.put("labels", new ArrayList()); + vals.put("keys", new ArrayList()); + push(vals); + } + + public final void end(String name) { + Map vals = (Map) pop(); + Label dflt = getLabel(vals.get("dflt")); + List keyList = (List) vals.get("keys"); + List lbls = (List) vals.get("labels"); + Label[] labels = (Label[]) lbls.toArray(new Label[lbls.size()]); + int[] keys = new int[keyList.size()]; + for (int i = 0; i < keys.length; i++) { + keys[i] = Integer.parseInt((String) keyList.get(i)); + } + getCodeVisitor().visitLookupSwitchInsn(dflt, keys, labels); + } + + } + + /** + * LookupSwitchLabelRule + */ + private final class LookupSwitchLabelRule extends Rule { + + public final void begin(String name, Attributes attrs) { + Map vals = (Map) peek(); + ((List) vals.get("labels")).add(getLabel(attrs.getValue("name"))); + ((List) vals.get("keys")).add(attrs.getValue("key")); + } + + } + + /** + * LabelRule + */ + private final class LabelRule extends Rule { + + public final void begin(String name, Attributes attrs) { + getCodeVisitor().visitLabel(getLabel(attrs.getValue("name"))); + } + + } + + /** + * TryCatchRule + */ + private final class TryCatchRule extends Rule { + + public final void begin(String name, Attributes attrs) { + Label start = getLabel(attrs.getValue("start")); + Label end = getLabel(attrs.getValue("end")); + Label handler = getLabel(attrs.getValue("handler")); + String type = attrs.getValue("type"); + getCodeVisitor().visitTryCatchBlock(start, end, handler, type); + } + + } + + /** + * LineNumberRule + */ + private final class LineNumberRule extends Rule { + + public final void begin(String name, Attributes attrs) { + int line = Integer.parseInt(attrs.getValue("line")); + Label start = getLabel(attrs.getValue("start")); + getCodeVisitor().visitLineNumber(line, start); + } + + } + + /** + * LocalVarRule + */ + private final class LocalVarRule extends Rule { + + public final void begin(String element, Attributes attrs) { + String name = attrs.getValue("name"); + String desc = attrs.getValue("desc"); + String signature = attrs.getValue("signature"); + Label start = getLabel(attrs.getValue("start")); + Label end = getLabel(attrs.getValue("end")); + int var = Integer.parseInt(attrs.getValue("var")); + getCodeVisitor().visitLocalVariable(name, + desc, + signature, + start, + end, + var); + } + + } + + /** + * OpcodesRule + */ + private final class OpcodesRule extends Rule { + + // public boolean match( String match, String element) { + // return match.startsWith( path) && OPCODES.containsKey( element); + // } + + public final void begin(String element, Attributes attrs) { + Opcode o = ((Opcode) OPCODES.get(element)); + if (o == null) + return; + + switch (o.type) { + case OpcodeGroup.INSN: + getCodeVisitor().visitInsn(o.opcode); + break; + + case OpcodeGroup.INSN_FIELD: + getCodeVisitor().visitFieldInsn(o.opcode, + attrs.getValue("owner"), + attrs.getValue("name"), + attrs.getValue("desc")); + break; + + case OpcodeGroup.INSN_INT: + getCodeVisitor().visitIntInsn(o.opcode, + Integer.parseInt(attrs.getValue("value"))); + break; + + case OpcodeGroup.INSN_JUMP: + getCodeVisitor().visitJumpInsn(o.opcode, + getLabel(attrs.getValue("label"))); + break; + + case OpcodeGroup.INSN_METHOD: + getCodeVisitor().visitMethodInsn(o.opcode, + attrs.getValue("owner"), + attrs.getValue("name"), + attrs.getValue("desc")); + break; + + case OpcodeGroup.INSN_TYPE: + getCodeVisitor().visitTypeInsn(o.opcode, + attrs.getValue("desc")); + break; + + case OpcodeGroup.INSN_VAR: + getCodeVisitor().visitVarInsn(o.opcode, + Integer.parseInt(attrs.getValue("var"))); + break; + + case OpcodeGroup.INSN_IINC: + getCodeVisitor().visitIincInsn(Integer.parseInt(attrs.getValue("var")), + Integer.parseInt(attrs.getValue("inc"))); + break; + + case OpcodeGroup.INSN_LDC: + getCodeVisitor().visitLdcInsn(getValue(attrs.getValue("desc"), + attrs.getValue("cst"))); + break; + + case OpcodeGroup.INSN_MULTIANEWARRAY: + getCodeVisitor().visitMultiANewArrayInsn(attrs.getValue("desc"), + Integer.parseInt(attrs.getValue("dims"))); + break; + + default: + throw new RuntimeException("Invalid element: " + element + + " at " + match); + + } + } + } + + /** + * MaxRule + */ + private final class MaxRule extends Rule { + + public final void begin(String element, Attributes attrs) { + int maxStack = Integer.parseInt(attrs.getValue("maxStack")); + int maxLocals = Integer.parseInt(attrs.getValue("maxLocals")); + getCodeVisitor().visitMaxs(maxStack, maxLocals); + } + + } + + private final class AnnotationRule extends Rule { + + public void begin(String name, Attributes attrs) { + String desc = attrs.getValue("desc"); + boolean visible = Boolean.valueOf(attrs.getValue("visible")) + .booleanValue(); + + Object v = peek(); + if (v instanceof ClassVisitor) { + push(((ClassVisitor) v).visitAnnotation(desc, visible)); + } else if (v instanceof FieldVisitor) { + push(((FieldVisitor) v).visitAnnotation(desc, visible)); + } else if (v instanceof MethodVisitor) { + push(((MethodVisitor) v).visitAnnotation(desc, visible)); + } + } + + public void end(String name) { + ((AnnotationVisitor) pop()).visitEnd(); + } + + } + + private final class AnnotationParameterRule extends Rule { + + public void begin(String name, Attributes attrs) { + int parameter = Integer.parseInt(attrs.getValue("parameter")); + String desc = attrs.getValue("desc"); + boolean visible = Boolean.valueOf(attrs.getValue("visible")) + .booleanValue(); + + push(((MethodVisitor) peek()).visitParameterAnnotation(parameter, + desc, + visible)); + } + + public void end(String name) { + ((AnnotationVisitor) pop()).visitEnd(); + } + + } + + private final class AnnotationValueRule extends Rule { + + public void begin(String nm, Attributes attrs) { + String name = attrs.getValue("name"); + String desc = attrs.getValue("desc"); + String value = attrs.getValue("value"); + ((AnnotationVisitor) peek()).visit(name, getValue(desc, value)); + } + + } + + private final class AnnotationValueEnumRule extends Rule { + + public void begin(String nm, Attributes attrs) { + String name = attrs.getValue("name"); + String desc = attrs.getValue("desc"); + String value = attrs.getValue("value"); + ((AnnotationVisitor) peek()).visitEnum(name, desc, value); + } + + } + + private final class AnnotationValueAnnotationRule extends Rule { + + public void begin(String nm, Attributes attrs) { + String name = attrs.getValue("name"); + String desc = attrs.getValue("desc"); + push(((AnnotationVisitor) peek()).visitAnnotation(name, desc)); + } + + public void end(String name) { + ((AnnotationVisitor) pop()).visitEnd(); + } + + } + + private final class AnnotationValueArrayRule extends Rule { + + public void begin(String nm, Attributes attrs) { + String name = attrs.getValue("name"); + push(((AnnotationVisitor) peek()).visitArray(name)); + } + + public void end(String name) { + ((AnnotationVisitor) pop()).visitEnd(); + } + + } + + private final class AnnotationDefaultRule extends Rule { + + public void begin(String nm, Attributes attrs) { + push(((MethodVisitor) peek()).visitAnnotationDefault()); + } + + public void end(String name) { + ((AnnotationVisitor) pop()).visitEnd(); + } + + } + + /** + * Opcode + */ + private final static class Opcode { + public int opcode; + + public int type; + + public Opcode(int opcode, int type) { + this.opcode = opcode; + this.type = type; + } + + } + +}
diff --git a/asmx/src/org/objectweb/asm/xml/Processor.java b/asmx/src/org/objectweb/asm/xml/Processor.java new file mode 100644 index 0000000..2baeb0f --- /dev/null +++ b/asmx/src/org/objectweb/asm/xml/Processor.java
@@ -0,0 +1,1048 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004, 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 org.objectweb.asm.xml; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import javax.xml.transform.Source; +import javax.xml.transform.Templates; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXSource; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamSource; + +import org.objectweb.asm.ClassReader; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.ext.LexicalHandler; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.DefaultHandler; +import org.xml.sax.helpers.XMLReaderFactory; + +/** + * Processor is a command line tool that can be used for bytecode waving + * directed by XSL transformation. <p> In order to use a concrete XSLT engine, + * system property <tt>javax.xml.transform.TransformerFactory</tt> must be set + * to one of the following values. + * + * <blockquote> <table border="1" cellspacing="0" cellpadding="3"> <tr> <td>jd.xslt</td> + * <td>jd.xml.xslt.trax.TransformerFactoryImpl</td> </tr> + * + * <tr> <td>Saxon</td> <td>net.sf.saxon.TransformerFactoryImpl</td> </tr> + * + * <tr> <td>Caucho</td> <td>com.caucho.xsl.Xsl</td> </tr> + * + * <tr> <td>Xalan interpeter</td> <td>org.apache.xalan.processor.TransformerFactory</td> + * </tr> + * + * <tr> <td>Xalan xsltc</td> <td>org.apache.xalan.xsltc.trax.TransformerFactoryImpl</td> + * </tr> </table> </blockquote> + * + * @author Eugene Kuleshov + */ +public class Processor { + + public static final int BYTECODE = 1; + + public static final int MULTI_XML = 2; + + public static final int SINGLE_XML = 3; + + private static final String SINGLE_XML_NAME = "classes.xml"; + + private int inRepresentation; + + private int outRepresentation; + + private InputStream input = null; + + private OutputStream output = null; + + private Source xslt = null; + + private boolean computeMax; + + private int n = 0; + + public Processor( + int inRepresenation, + int outRepresentation, + InputStream input, + OutputStream output, + Source xslt) + { + this.inRepresentation = inRepresenation; + this.outRepresentation = outRepresentation; + this.input = input; + this.output = output; + this.xslt = xslt; + this.computeMax = true; + } + + public int process() throws TransformerException, IOException, SAXException + { + ZipInputStream zis = new ZipInputStream(input); + final ZipOutputStream zos = new ZipOutputStream(output); + final OutputStreamWriter osw = new OutputStreamWriter(zos); + + Thread.currentThread() + .setContextClassLoader(getClass().getClassLoader()); + + TransformerFactory tf = TransformerFactory.newInstance(); + if (!tf.getFeature(SAXSource.FEATURE) + || !tf.getFeature(SAXResult.FEATURE)) + return 0; + + SAXTransformerFactory saxtf = (SAXTransformerFactory) tf; + Templates templates = null; + if (xslt != null) { + templates = saxtf.newTemplates(xslt); + } + + // configuring outHandlerFactory + // /////////////////////////////////////////////////////// + + EntryElement entryElement = getEntryElement(zos); + + ContentHandler outDocHandler = null; + switch (outRepresentation) { + case BYTECODE: + outDocHandler = new OutputSlicingHandler(new ASMContentHandlerFactory(zos, + computeMax), + entryElement, + false); + break; + + case MULTI_XML: + outDocHandler = new OutputSlicingHandler(new SAXWriterFactory(osw, + true), + entryElement, + true); + break; + + case SINGLE_XML: + ZipEntry outputEntry = new ZipEntry(SINGLE_XML_NAME); + zos.putNextEntry(outputEntry); + outDocHandler = new SAXWriter(osw, false); + break; + + } + + // configuring inputDocHandlerFactory + // ///////////////////////////////////////////////// + ContentHandler inDocHandler = null; + if (templates == null) { + inDocHandler = outDocHandler; + } else { + inDocHandler = new InputSlicingHandler("class", + outDocHandler, + new TransformerHandlerFactory(saxtf, + templates, + outDocHandler)); + } + ContentHandlerFactory inDocHandlerFactory = new SubdocumentHandlerFactory(inDocHandler); + + if (inDocHandler != null && inRepresentation != SINGLE_XML) { + inDocHandler.startDocument(); + inDocHandler.startElement("", + "classes", + "classes", + new AttributesImpl()); + } + + int i = 0; + ZipEntry ze = null; + while ((ze = zis.getNextEntry()) != null) { + update(ze.getName(), n++); + if (isClassEntry(ze)) { + processEntry(zis, ze, inDocHandlerFactory); + } else { + OutputStream os = entryElement.openEntry(getName(ze)); + copyEntry(zis, os); + entryElement.closeEntry(); + } + + i++; + } + + if (inDocHandler != null && inRepresentation != SINGLE_XML) { + inDocHandler.endElement("", "classes", "classes"); + inDocHandler.endDocument(); + } + + if (outRepresentation == SINGLE_XML) { + zos.closeEntry(); + } + zos.flush(); + zos.close(); + + return i; + } + + private void copyEntry(InputStream is, OutputStream os) throws IOException { + if (outRepresentation == SINGLE_XML) + return; + + byte[] buff = new byte[2048]; + int i; + while ((i = is.read(buff)) != -1) { + os.write(buff, 0, i); + } + } + + private boolean isClassEntry(ZipEntry ze) { + String name = ze.getName(); + return inRepresentation == SINGLE_XML && name.equals(SINGLE_XML_NAME) + || name.endsWith(".class") || name.endsWith(".class.xml"); + } + + private void processEntry( + final ZipInputStream zis, + ZipEntry ze, + ContentHandlerFactory handlerFactory) + { + ContentHandler handler = handlerFactory.createContentHandler(); + try { + + // if (CODE2ASM.equals(command)) { // read bytecode and process it + // // with TraceClassVisitor + // ClassReader cr = new ClassReader(readEntry(zis, ze)); + // cr.accept(new TraceClassVisitor(null, new PrintWriter(os)), + // false); + // } + + boolean singleInputDocument = inRepresentation == SINGLE_XML; + if (inRepresentation == BYTECODE) { // read bytecode and process it + // with handler + ClassReader cr = new ClassReader(readEntry(zis, ze)); + cr.accept(new SAXClassAdapter(handler, singleInputDocument), + false); + + } else { // read XML and process it with handler + XMLReader reader = XMLReaderFactory.createXMLReader(); + reader.setContentHandler(handler); + reader.parse(new InputSource(singleInputDocument + ? (InputStream) new ProtectedInputStream(zis) + : new ByteArrayInputStream(readEntry(zis, ze)))); + + } + } catch (Exception ex) { + update(ze.getName(), 0); + update(ex, 0); + } + } + + private EntryElement getEntryElement(ZipOutputStream zos) { + if (outRepresentation == SINGLE_XML) { + return new SingleDocElement(zos); + } + return new ZipEntryElement(zos); + } + + // private ContentHandlerFactory getHandlerFactory( + // OutputStream os, + // SAXTransformerFactory saxtf, + // Templates templates) + // { + // ContentHandlerFactory factory = null; + // if (templates == null) { + // if (outputRepresentation == BYTECODE) { // factory used to write + // // bytecode + // factory = new ASMContentHandlerFactory(os, computeMax); + // } else { // factory used to write XML + // factory = new SAXWriterFactory(os, true); + // } + // } else { + // if (outputRepresentation == BYTECODE) { // factory used to transform + // // and then write bytecode + // factory = new ASMTransformerHandlerFactory(saxtf, + // templates, + // os, + // computeMax); + // } else { // factory used to transformand then write XML + // factory = new TransformerHandlerFactory(saxtf, + // templates, + // os, + // outputRepresentation == SINGLE_XML); + // } + // } + // return factory; + // } + + private String getName(ZipEntry ze) { + String name = ze.getName(); + if (isClassEntry(ze)) { + if (inRepresentation != BYTECODE && outRepresentation == BYTECODE) { + name = name.substring(0, name.length() - 4); // .class.xml to + // .class + } else if (inRepresentation == BYTECODE + && outRepresentation != BYTECODE) + { + name = name.concat(".xml"); // .class to .class.xml + } + // } else if( CODE2ASM.equals( command)) { + // name = name.substring( 0, name.length()-6).concat( ".asm"); + } + return name; + } + + private byte[] readEntry(ZipInputStream zis, ZipEntry ze) + throws IOException + { + long size = ze.getSize(); + if (size > -1) { + byte[] buff = new byte[(int) size]; + int k = 0; + int n; + while(( n = zis.read(buff, k, buff.length-k)) > 0) { + k += n; + } + return buff; + } + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + byte[] buff = new byte[4096]; + int i; + while ((i = zis.read(buff)) != -1) { + bos.write(buff, 0, i); + } + return bos.toByteArray(); + } + + /* + * (non-Javadoc) + * + * @see java.util.Observer#update(java.util.Observable, java.lang.Object) + */ + protected void update(Object arg, int n) { + if (arg instanceof Throwable) { + ((Throwable) arg).printStackTrace(); + } else { + if ((n % 100) == 0) { + System.err.println(n + " " + arg); + } + } + } + + public static void main(String[] args) throws Exception { + if (args.length < 2) { + showUsage(); + return; + } + + int inRepresentation = getRepresentation(args[0]); + int outRepresentation = getRepresentation(args[1]); + + InputStream is = System.in; + OutputStream os = new BufferedOutputStream(System.out); + + Source xslt = null; + // boolean computeMax = true; + + for (int i = 2; i < args.length; i++) { + if ("-in".equals(args[i])) { + is = new FileInputStream(args[++i]); + + } else if ("-out".equals(args[i])) { + os = new BufferedOutputStream(new FileOutputStream(args[++i])); + + } else if ("-xslt".equals(args[i])) { + xslt = new StreamSource(new FileInputStream(args[++i])); + + // } else if( "-computemax".equals( args[ i].toLowerCase())) { + // computeMax = true; + + } else { + showUsage(); + return; + + } + } + + if (inRepresentation == 0 || outRepresentation == 0) { + showUsage(); + return; + } + + Processor m = new Processor(inRepresentation, + outRepresentation, + is, + os, + xslt); + + long l1 = System.currentTimeMillis(); + int n = m.process(); + long l2 = System.currentTimeMillis(); + System.err.println(n); + System.err.println("" + (l2 - l1) + "ms " + (1000f * n / (l2 - l1)) + + " resources/sec"); + } + + private static int getRepresentation(String s) { + if ("code".equals(s)) { + return BYTECODE; + } else if ("xml".equals(s)) { + return MULTI_XML; + } else if ("singlexml".equals(s)) { + return SINGLE_XML; + } + return 0; + } + + private static void showUsage() { + System.err.println("Usage: Main <in format> <out format> [-in <input jar>] [-out <output jar>] [-xslt <xslt file>]"); + System.err.println(" when -in or -out is omitted sysin and sysout would be used"); + System.err.println(" <in format> and <out format> - code | xml | singlexml"); + } + + /** + * IputStream wrapper class used to protect input streams from being closed + * by some stupid XML parsers. + */ + private static final class ProtectedInputStream extends InputStream { + private final InputStream is; + + private ProtectedInputStream(InputStream is) { + super(); + this.is = is; + } + + public final void close() throws IOException { + } + + public final int read() throws IOException { + return is.read(); + } + + public final int read(byte[] b, int off, int len) throws IOException { + return is.read(b, off, len); + } + + public final int available() throws IOException { + return is.available(); + } + } + + /** + * A {@link ContentHandlerFactory ContentHandlerFactory} is used to create + * {@link org.xml.sax.ContentHandler ContentHandler} instances for concrete + * context. + */ + private static interface ContentHandlerFactory { + + /** + * Creates an instance of the content handler. + * + * @return content handler + */ + ContentHandler createContentHandler(); + + } + + /** + * SAXWriterFactory + */ + private static final class SAXWriterFactory implements + ContentHandlerFactory + { + private Writer w; + + private boolean optimizeEmptyElements; + + public SAXWriterFactory(Writer w, boolean optimizeEmptyElements) { + this.w = w; + this.optimizeEmptyElements = optimizeEmptyElements; + } + + public final ContentHandler createContentHandler() { + return new SAXWriter(w, optimizeEmptyElements); + } + + } + + /** + * ASMContentHandlerFactory + */ + private static final class ASMContentHandlerFactory implements + ContentHandlerFactory + { + private OutputStream os; + + private boolean computeMax; + + public ASMContentHandlerFactory(OutputStream os, boolean computeMax) { + this.os = os; + this.computeMax = computeMax; + } + + public final ContentHandler createContentHandler() { + return new ASMContentHandler(os, computeMax); + } + + } + + /** + * TransformerHandlerFactory + */ + private static final class TransformerHandlerFactory implements + ContentHandlerFactory + { + private SAXTransformerFactory saxtf; + + private Templates templates; + + private ContentHandler outputHandler; + + public TransformerHandlerFactory( + SAXTransformerFactory saxtf, + Templates templates, + ContentHandler outputHandler) + { + this.saxtf = saxtf; + this.templates = templates; + this.outputHandler = outputHandler; + } + + public final ContentHandler createContentHandler() { + try { + TransformerHandler handler = saxtf.newTransformerHandler(templates); + handler.setResult(new SAXResult(outputHandler)); + return handler; + } catch (TransformerConfigurationException ex) { + throw new RuntimeException(ex.toString()); + } + } + } + + /** + * SubdocumentHandlerFactory + */ + private final static class SubdocumentHandlerFactory implements + ContentHandlerFactory + { + private ContentHandler subdocumentHandler; + + public SubdocumentHandlerFactory(ContentHandler subdocumentHandler) { + this.subdocumentHandler = subdocumentHandler; + } + + public final ContentHandler createContentHandler() { + return subdocumentHandler; + } + + } + + /** + * A {@link org.xml.sax.ContentHandler ContentHandler} and + * {@link org.xml.sax.ext.LexicalHandler LexicalHandler} that serializes XML + * from SAX 2.0 events into {@link java.io.Writer Writer}. + * + * <i><blockquote> This implementation does not support namespaces, entity + * definitions (uncluding DTD), CDATA and text elements. </blockquote></i> + */ + private final static class SAXWriter extends DefaultHandler implements + LexicalHandler + { + private static final char[] OFF = " ".toCharArray(); + + private Writer w; + + private boolean optimizeEmptyElements; + + private boolean openElement = false; + + private int ident = 0; + + /** + * Creates <code>SAXWriter</code>. + * + * @param w writer + * @param optimizeEmptyElements if set to <code>true</code>, short + * XML syntax will be used for empty elements + */ + public SAXWriter(Writer w, boolean optimizeEmptyElements) { + this.w = w; + this.optimizeEmptyElements = optimizeEmptyElements; + } + + public final void startElement( + String ns, + String localName, + String qName, + Attributes atts) throws SAXException + { + try { + closeElement(); + + writeIdent(); + w.write("<".concat(qName)); + if (atts != null && atts.getLength() > 0) + writeAttributes(atts); + + if (!optimizeEmptyElements) { + w.write(">\n"); + } else { + openElement = true; + } + ident += 2; + + } catch (IOException ex) { + throw new SAXException(ex); + + } + } + + public final void endElement(String ns, String localName, String qName) + throws SAXException + { + ident -= 2; + try { + if (openElement) { + w.write("/>\n"); + openElement = false; + } else { + writeIdent(); + w.write("</" + qName + ">\n"); + } + + } catch (IOException ex) { + throw new SAXException(ex); + + } + } + + public final void endDocument() throws SAXException { + try { + w.flush(); + + } catch (IOException ex) { + throw new SAXException(ex); + + } + } + + public final void comment(char[] ch, int off, int len) + throws SAXException + { + try { + closeElement(); + + writeIdent(); + w.write("<!-- "); + w.write(ch, off, len); + w.write(" -->\n"); + + } catch (IOException ex) { + throw new SAXException(ex); + + } + } + + public final void startDTD(String arg0, String arg1, String arg2) + throws SAXException + { + } + + public final void endDTD() throws SAXException { + } + + public final void startEntity(String arg0) throws SAXException { + } + + public final void endEntity(String arg0) throws SAXException { + } + + public final void startCDATA() throws SAXException { + } + + public final void endCDATA() throws SAXException { + } + + private final void writeAttributes(Attributes atts) throws IOException { + StringBuffer sb = new StringBuffer(); + int len = atts.getLength(); + for (int i = 0; i < len; i++) { + sb.append(" ") + .append(atts.getLocalName(i)) + .append("=\"") + .append(esc(atts.getValue(i))) + .append("\""); + } + w.write(sb.toString()); + } + + /** + * Encode string with escaping. + * + * @param str string to encode. + * @return encoded string + */ + private final String esc(String str) { + StringBuffer sb = new StringBuffer(str.length()); + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + switch (ch) { + case '&': + sb.append("&"); + break; + + case '<': + sb.append("<"); + break; + + case '>': + sb.append(">"); + break; + + case '\"': + sb.append("""); + break; + + default: + if (ch > 0x7f) { + sb.append("&#") + .append(Integer.toString(ch)) + .append(';'); + } else { + sb.append(ch); + } + + } + } + return sb.toString(); + } + + private final void writeIdent() throws IOException { + int n = ident; + while (n > 0) { + if (n > OFF.length) { + w.write(OFF); + n -= OFF.length; + } else { + w.write(OFF, 0, n); + n = 0; + } + } + } + + private final void closeElement() throws IOException { + if (openElement) { + w.write(">\n"); + } + openElement = false; + } + + } + + /** + * A {@link org.xml.sax.ContentHandler ContentHandler} that splits XML + * documents into smaller chunks. Each chunk is processed by the nested + * {@link org.xml.sax.ContentHandler ContentHandler} obtained from + * {@link java.net.ContentHandlerFactory ContentHandlerFactory}. This is + * useful for running XSLT engine against large XML document that will + * hardly fit into the memory all together. <p> TODO use complete path for + * subdocumentRoot + */ + private final static class InputSlicingHandler extends DefaultHandler { + private String subdocumentRoot; + + private ContentHandler rootHandler; + + private ContentHandlerFactory subdocumentHandlerFactory; + + private boolean subdocument = false; + + private ContentHandler subdocumentHandler; + + /** + * Constructs a new {@link InputSlicingHandler SubdocumentHandler} + * object. + * + * @param subdocumentRoot name/path to the root element of the + * subdocument + * @param rootHandler content handler for the entire document + * (subdocument envelope). + * @param subdocumentHandlerFactory a + * {@link ContentHandlerFactory ContentHandlerFactory} used to + * create {@link ContentHandler ContentHandler} instances for + * subdocuments. + */ + public InputSlicingHandler( + String subdocumentRoot, + ContentHandler rootHandler, + ContentHandlerFactory subdocumentHandlerFactory) + { + this.subdocumentRoot = subdocumentRoot; + this.rootHandler = rootHandler; + this.subdocumentHandlerFactory = subdocumentHandlerFactory; + } + + public final void startElement( + String namespaceURI, + String localName, + String qName, + Attributes list) throws SAXException + { + if (subdocument) { + subdocumentHandler.startElement(namespaceURI, + localName, + qName, + list); + } else if (localName.equals(subdocumentRoot)) { + subdocumentHandler = subdocumentHandlerFactory.createContentHandler(); + subdocumentHandler.startDocument(); + subdocumentHandler.startElement(namespaceURI, + localName, + qName, + list); + subdocument = true; + } else if (rootHandler != null) { + rootHandler.startElement(namespaceURI, localName, qName, list); + } + } + + public final void endElement( + String namespaceURI, + String localName, + String qName) throws SAXException + { + if (subdocument) { + subdocumentHandler.endElement(namespaceURI, localName, qName); + if (localName.equals(subdocumentRoot)) { + subdocumentHandler.endDocument(); + subdocument = false; + } + } else if (rootHandler != null) { + rootHandler.endElement(namespaceURI, localName, qName); + } + } + + public final void startDocument() throws SAXException { + if (rootHandler != null) { + rootHandler.startDocument(); + } + } + + public final void endDocument() throws SAXException { + if (rootHandler != null) { + rootHandler.endDocument(); + + } + } + + public final void characters(char[] buff, int offset, int size) + throws SAXException + { + if (subdocument) { + subdocumentHandler.characters(buff, offset, size); + } else if (rootHandler != null) { + rootHandler.characters(buff, offset, size); + } + } + + } + + /** + * A {@link org.xml.sax.ContentHandler ContentHandler} that splits XML + * documents into smaller chunks. Each chunk is processed by the nested + * {@link org.xml.sax.ContentHandler ContentHandler} obtained from + * {@link java.net.ContentHandlerFactory ContentHandlerFactory}. This is + * useful for running XSLT engine against large XML document that will + * hardly fit into the memory all together. <p> TODO use complete path for + * subdocumentRoot + */ + private static final class OutputSlicingHandler extends DefaultHandler { + private String subdocumentRoot; + + private ContentHandlerFactory subdocumentHandlerFactory; + + private EntryElement entryElement; + + private boolean isXml; + + private boolean subdocument = false; + + private ContentHandler subdocumentHandler; + + /** + * Constructs a new {@link OutputSlicingHandler SubdocumentHandler} + * object. + * + * @param subdocumentHandlerFactory a + * {@link ContentHandlerFactory ContentHandlerFactory} used to + * create {@link ContentHandler ContentHandler} instances for + * subdocuments. + * @param entryElement TODO. + * @param isXml TODO. + */ + public OutputSlicingHandler( + ContentHandlerFactory subdocumentHandlerFactory, + EntryElement entryElement, + boolean isXml) + { + this.subdocumentRoot = "class"; + this.subdocumentHandlerFactory = subdocumentHandlerFactory; + this.entryElement = entryElement; + this.isXml = isXml; + } + + public final void startElement( + String namespaceURI, + String localName, + String qName, + Attributes list) throws SAXException + { + if (subdocument) { + subdocumentHandler.startElement(namespaceURI, + localName, + qName, + list); + } else if (localName.equals(subdocumentRoot)) { + String name = list.getValue("name"); + if (name == null || name.length() == 0) + throw new SAXException("Class element without name attribute."); + try { + entryElement.openEntry(isXml + ? name.concat(".class.xml") + : name.concat(".class")); + } catch (IOException ex) { + throw new SAXException(ex.toString(), ex); + } + subdocumentHandler = subdocumentHandlerFactory.createContentHandler(); + subdocumentHandler.startDocument(); + subdocumentHandler.startElement(namespaceURI, + localName, + qName, + list); + subdocument = true; + } + } + + public final void endElement( + String namespaceURI, + String localName, + String qName) throws SAXException + { + if (subdocument) { + subdocumentHandler.endElement(namespaceURI, localName, qName); + if (localName.equals(subdocumentRoot)) { + subdocumentHandler.endDocument(); + subdocument = false; + try { + entryElement.closeEntry(); + } catch (IOException ex) { + throw new SAXException(ex.toString(), ex); + } + } + } + } + + public final void startDocument() throws SAXException { + } + + public final void endDocument() throws SAXException { + } + + public final void characters(char[] buff, int offset, int size) + throws SAXException + { + if (subdocument) { + subdocumentHandler.characters(buff, offset, size); + } + } + + } + + private static interface EntryElement { + + OutputStream openEntry(String name) throws IOException; + + void closeEntry() throws IOException; + + } + + private static final class SingleDocElement implements EntryElement { + private OutputStream os; + + public SingleDocElement(OutputStream os) { + this.os = os; + } + + public OutputStream openEntry(String name) throws IOException { + return os; + } + + public void closeEntry() throws IOException { + os.flush(); + } + + } + + private static final class ZipEntryElement implements EntryElement { + private ZipOutputStream zos; + + public ZipEntryElement(ZipOutputStream zos) { + this.zos = zos; + } + + public OutputStream openEntry(String name) throws IOException { + ZipEntry entry = new ZipEntry(name); + zos.putNextEntry(entry); + return zos; + } + + public void closeEntry() throws IOException { + zos.flush(); + zos.closeEntry(); + } + + } + +}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXAdapter.java new file mode 100644 index 0000000..c8f6ecb --- /dev/null +++ b/asmx/src/org/objectweb/asm/xml/SAXAdapter.java
@@ -0,0 +1,91 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004, 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 org.objectweb.asm.xml; + +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; + +/** + * SAXAdapter + * + * @author Eugene Kuleshov + */ +public abstract class SAXAdapter { + private final ContentHandler h; + + public SAXAdapter(ContentHandler h) { + this.h = h; + } + + protected ContentHandler getContentHandler() { + return h; + } + + protected final void addEnd(String name) { + try { + h.endElement("", name, name); + } catch (SAXException ex) { + throw new RuntimeException(ex.toString()); + } + } + + protected final void addStart(String name, Attributes attrs) { + try { + h.startElement("", name, name, attrs); + } catch (SAXException ex) { + throw new RuntimeException(ex.toString()); + } + } + + protected final void addElement(String name, Attributes attrs) { + addStart(name, attrs); + addEnd(name); + } + + protected void addDocumentStart() { + try { + h.startDocument(); + } catch (SAXException ex) { + throw new RuntimeException(ex.getException()); + } + } + + protected void addDocumentEnd() { + try { + h.endDocument(); + } catch (SAXException ex) { + // ex.getException().printStackTrace(); + // ex.printStackTrace(); + throw new RuntimeException(ex.toString()); + } + } + +}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXAnnotationAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXAnnotationAdapter.java new file mode 100644 index 0000000..e738241 --- /dev/null +++ b/asmx/src/org/objectweb/asm/xml/SAXAnnotationAdapter.java
@@ -0,0 +1,191 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004, 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 org.objectweb.asm.xml; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Type; +import org.xml.sax.ContentHandler; +import org.xml.sax.helpers.AttributesImpl; + +/** + * SAXAnnotationAdapter + * + * @author Eugene Kuleshov + */ +public class SAXAnnotationAdapter extends SAXAdapter implements + AnnotationVisitor +{ + private final String elementName; + + public SAXAnnotationAdapter( + ContentHandler h, + String elementName, + int visible, + String name, + String desc) + { + this(h, elementName, visible, desc, name, -1); + } + + public SAXAnnotationAdapter( + ContentHandler h, + String elementName, + int visible, + int parameter, + String desc) + { + this(h, elementName, visible, desc, null, parameter); + } + + private SAXAnnotationAdapter( + ContentHandler h, + String elementName, + int visible, + String desc, + String name, + int parameter) + { + super(h); + this.elementName = elementName; + + AttributesImpl att = new AttributesImpl(); + if (name != null) + att.addAttribute("", "name", "name", "", name); + if (visible != 0) + att.addAttribute("", "visible", "visible", "", visible > 0 + ? "true" + : "false"); + if (parameter != -1) + att.addAttribute("", + "parameter", + "parameter", + "", + Integer.toString(parameter)); + if (desc != null) + att.addAttribute("", "desc", "desc", "", desc); + + addStart(elementName, att); + } + + public void visit(String name, Object value) { + Class c = value.getClass(); + if (c.isArray()) { + AnnotationVisitor av = visitArray(name); + if (value instanceof byte[]) { + byte[] b = (byte[]) value; + for (int i = 0; i < b.length; i++) + av.visit(null, new Byte(b[i])); + + } else if (value instanceof char[]) { + char[] b = (char[]) value; + for (int i = 0; i < b.length; i++) + av.visit(null, new Character(b[i])); + + } else if (value instanceof boolean[]) { + boolean[] b = (boolean[]) value; + for (int i = 0; i < b.length; i++) + av.visit(null, Boolean.valueOf(b[i])); + + } else if (value instanceof int[]) { + int[] b = (int[]) value; + for (int i = 0; i < b.length; i++) + av.visit(null, new Integer(b[i])); + + } else if (value instanceof long[]) { + long[] b = (long[]) value; + for (int i = 0; i < b.length; i++) + av.visit(null, new Long(b[i])); + + } else if (value instanceof float[]) { + float[] b = (float[]) value; + for (int i = 0; i < b.length; i++) + av.visit(null, new Float(b[i])); + + } else if (value instanceof double[]) { + double[] b = (double[]) value; + for (int i = 0; i < b.length; i++) + av.visit(null, new Double(b[i])); + + } + av.visitEnd(); + } else { + addValueElement("annotationValue", + name, + Type.getDescriptor(c), + value.toString()); + } + } + + public void visitEnum(String name, String desc, String value) { + addValueElement("annotationValueEnum", name, desc, value); + } + + public AnnotationVisitor visitAnnotation(String name, String desc) { + return new SAXAnnotationAdapter(getContentHandler(), + "annotationValueAnnotation", + 0, + name, + desc); + } + + public AnnotationVisitor visitArray(String name) { + return new SAXAnnotationAdapter(getContentHandler(), + "annotationValueArray", + 0, + name, + null); + } + + public void visitEnd() { + addEnd(elementName); + } + + private void addValueElement( + String element, + String name, + String desc, + String value) + { + AttributesImpl att = new AttributesImpl(); + if (name != null) + att.addAttribute("", "name", "name", "", name); + if (desc != null) + att.addAttribute("", "desc", "desc", "", desc); + if (value != null) + att.addAttribute("", + "value", + "value", + "", + SAXClassAdapter.encode(value)); + + addElement(element, att); + } + +}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXClassAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXClassAdapter.java new file mode 100644 index 0000000..ed7fd6a --- /dev/null +++ b/asmx/src/org/objectweb/asm/xml/SAXClassAdapter.java
@@ -0,0 +1,357 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004, 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 org.objectweb.asm.xml; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.xml.sax.ContentHandler; +import org.xml.sax.helpers.AttributesImpl; + +/** + * A {@link org.objectweb.asm.ClassVisitor ClassVisitor} that generates SAX 2.0 + * events from the visited class. It can feed any kind of + * {@link org.xml.sax.ContentHandler ContentHandler}, e.g. XML serializer, XSLT + * or XQuery engines. + * + * @see org.objectweb.asm.xml.Processor + * @see org.objectweb.asm.xml.ASMContentHandler + * + * @author Eugene Kuleshov + */ +public final class SAXClassAdapter extends SAXAdapter implements ClassVisitor { + private boolean singleDocument; + + /** + * Constructs a new {@link SAXClassAdapter SAXClassAdapter} object. + * + * @param h content handler that will be used to send SAX 2.0 events. + * @param singleDocument if <tt>true</tt> adapter will not produce + * {@link ContentHandler#startDocument() startDocument()} and + * {@link ContentHandler#endDocument() endDocument()} events. + */ + public SAXClassAdapter(ContentHandler h, boolean singleDocument) { + super(h); + this.singleDocument = singleDocument; + if (!singleDocument) { + addDocumentStart(); + } + } + + public void visitSource(String source, String debug) { + if (source == null && debug == null) { + return; + } + + AttributesImpl att = new AttributesImpl(); + if (source != null) + att.addAttribute("", "file", "file", "", encode(source)); + if (debug != null) + att.addAttribute("", "debug", "debug", "", encode(debug)); + + addElement("source", att); + } + + public void visitOuterClass(String owner, String name, String desc) { + AttributesImpl att = new AttributesImpl(); + att.addAttribute("", "owner", "owner", "", owner); + if (name != null) + att.addAttribute("", "name", "name", "", name); + if (desc != null) + att.addAttribute("", "desc", "desc", "", desc); + + addElement("outerclass", att); + } + + public final void visitAttribute(Attribute attr) { + // TODO Auto-generated SAXClassAdapter.visitAttribute + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new SAXAnnotationAdapter(getContentHandler(), + "annotation", + visible ? 1 : -1, + null, + desc); + } + + public TypeAnnotationVisitor visitTypeAnnotation( + String desc, boolean visible, boolean inCode) { + throw new RuntimeException("Jaime did not implement yet"); + } + + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) + { + StringBuffer sb = new StringBuffer(); + if ((access & Opcodes.ACC_PUBLIC) != 0) + sb.append("public "); + if ((access & Opcodes.ACC_PRIVATE) != 0) + sb.append("private "); + if ((access & Opcodes.ACC_PROTECTED) != 0) + sb.append("protected "); + if ((access & Opcodes.ACC_FINAL) != 0) + sb.append("final "); + if ((access & Opcodes.ACC_SUPER) != 0) + sb.append("super "); + if ((access & Opcodes.ACC_INTERFACE) != 0) + sb.append("interface "); + if ((access & Opcodes.ACC_ABSTRACT) != 0) + sb.append("abstract "); + if ((access & Opcodes.ACC_SYNTHETIC) != 0) + sb.append("synthetic "); + if ((access & Opcodes.ACC_ANNOTATION) != 0) + sb.append("annotation "); + if ((access & Opcodes.ACC_ENUM) != 0) + sb.append("enum "); + if ((access & Opcodes.ACC_DEPRECATED) != 0) + sb.append("deprecated "); + + AttributesImpl att = new AttributesImpl(); + att.addAttribute("", "access", "access", "", sb.toString()); + if (name != null) + att.addAttribute("", "name", "name", "", name); + if (signature != null) + att.addAttribute("", + "signature", + "signature", + "", + encode(signature)); + if (superName != null) + att.addAttribute("", "parent", "parent", "", superName); + att.addAttribute("", + "major", + "major", + "", + Integer.toString(version & 0xFFFF)); + att.addAttribute("", + "minor", + "minor", + "", + Integer.toString(version >>> 16)); + addStart("class", att); + + addStart("interfaces", new AttributesImpl()); + if (interfaces != null && interfaces.length > 0) { + for (int i = 0; i < interfaces.length; i++) { + AttributesImpl att2 = new AttributesImpl(); + att2.addAttribute("", "name", "name", "", interfaces[i]); + addElement("interface", att2); + } + } + addEnd("interfaces"); + } + + public FieldVisitor visitField( + int access, + String name, + String desc, + String signature, + Object value) + { + StringBuffer sb = new StringBuffer(); + if ((access & Opcodes.ACC_PUBLIC) != 0) + sb.append("public "); + if ((access & Opcodes.ACC_PRIVATE) != 0) + sb.append("private "); + if ((access & Opcodes.ACC_PROTECTED) != 0) + sb.append("protected "); + if ((access & Opcodes.ACC_STATIC) != 0) + sb.append("static "); + if ((access & Opcodes.ACC_FINAL) != 0) + sb.append("final "); + if ((access & Opcodes.ACC_VOLATILE) != 0) + sb.append("volatile "); + if ((access & Opcodes.ACC_TRANSIENT) != 0) + sb.append("transient "); + if ((access & Opcodes.ACC_SYNTHETIC) != 0) + sb.append("synthetic "); + if ((access & Opcodes.ACC_ENUM) != 0) + sb.append("enum "); + if ((access & Opcodes.ACC_DEPRECATED) != 0) + sb.append("deprecated "); + + AttributesImpl att = new AttributesImpl(); + att.addAttribute("", "access", "access", "", sb.toString()); + att.addAttribute("", "name", "name", "", name); + att.addAttribute("", "desc", "desc", "", desc); + if (signature != null) + att.addAttribute("", + "signature", + "signature", + "", + encode(signature)); + if (value != null) { + att.addAttribute("", "value", "value", "", encode(value.toString())); + } + + return new SAXFieldAdapter(getContentHandler(), att); + } + + public MethodVisitor visitMethod( + int access, + String name, + String desc, + String signature, + String[] exceptions) + { + StringBuffer sb = new StringBuffer(); + if ((access & Opcodes.ACC_PUBLIC) != 0) + sb.append("public "); + if ((access & Opcodes.ACC_PRIVATE) != 0) + sb.append("private "); + if ((access & Opcodes.ACC_PROTECTED) != 0) + sb.append("protected "); + if ((access & Opcodes.ACC_STATIC) != 0) + sb.append("static "); + if ((access & Opcodes.ACC_FINAL) != 0) + sb.append("final "); + if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) + sb.append("synchronized "); + if ((access & Opcodes.ACC_BRIDGE) != 0) + sb.append("bridge "); + if ((access & Opcodes.ACC_VARARGS) != 0) + sb.append("varargs "); + if ((access & Opcodes.ACC_NATIVE) != 0) + sb.append("native "); + if ((access & Opcodes.ACC_ABSTRACT) != 0) + sb.append("abstract "); + if ((access & Opcodes.ACC_STRICT) != 0) + sb.append("strict "); + if ((access & Opcodes.ACC_SYNTHETIC) != 0) + sb.append("synthetic "); + if ((access & Opcodes.ACC_DEPRECATED) != 0) + sb.append("deprecated "); + + AttributesImpl att = new AttributesImpl(); + att.addAttribute("", "access", "access", "", sb.toString()); + att.addAttribute("", "name", "name", "", name); + att.addAttribute("", "desc", "desc", "", desc); + if (signature != null) { + att.addAttribute("", "signature", "signature", "", signature); + } + addStart("method", att); + + addStart("exceptions", new AttributesImpl()); + if (exceptions != null && exceptions.length > 0) { + for (int i = 0; i < exceptions.length; i++) { + AttributesImpl att2 = new AttributesImpl(); + att2.addAttribute("", "name", "name", "", exceptions[i]); + addElement("exception", att2); + } + } + addEnd("exceptions"); + + return new SAXCodeAdapter(getContentHandler(), access); + } + + public final void visitInnerClass( + String name, + String outerName, + String innerName, + int access) + { + StringBuffer sb = new StringBuffer(); + if ((access & Opcodes.ACC_PUBLIC) != 0) + sb.append("public "); + if ((access & Opcodes.ACC_PRIVATE) != 0) + sb.append("private "); + if ((access & Opcodes.ACC_PROTECTED) != 0) + sb.append("protected "); + if ((access & Opcodes.ACC_STATIC) != 0) + sb.append("static "); + if ((access & Opcodes.ACC_FINAL) != 0) + sb.append("final "); + if ((access & Opcodes.ACC_SUPER) != 0) + sb.append("super "); + if ((access & Opcodes.ACC_INTERFACE) != 0) + sb.append("interface "); + if ((access & Opcodes.ACC_ABSTRACT) != 0) + sb.append("abstract "); + if ((access & Opcodes.ACC_SYNTHETIC) != 0) + sb.append("synthetic "); + if ((access & Opcodes.ACC_ANNOTATION) != 0) + sb.append("annotation "); + if ((access & Opcodes.ACC_ENUM) != 0) + sb.append("enum "); + if ((access & Opcodes.ACC_DEPRECATED) != 0) + sb.append("deprecated "); + + AttributesImpl att = new AttributesImpl(); + att.addAttribute("", "access", "access", "", sb.toString()); + if (name != null) + att.addAttribute("", "name", "name", "", name); + if (outerName != null) + att.addAttribute("", "outerName", "outerName", "", outerName); + if (innerName != null) + att.addAttribute("", "innerName", "innerName", "", innerName); + addElement("innerclass", att); + } + + public final void visitEnd() { + addEnd("class"); + if (!singleDocument) { + addDocumentEnd(); + } + } + + static final String encode(String s) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '\\') { + sb.append("\\\\"); + } else if (c < 0x20 || c > 0x7f) { + sb.append("\\u"); + if (c < 0x10) { + sb.append("000"); + } else if (c < 0x100) { + sb.append("00"); + } else if (c < 0x1000) { + sb.append("0"); + } + sb.append(Integer.toString(c, 16)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + +}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXCodeAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXCodeAdapter.java new file mode 100644 index 0000000..ce7f83e --- /dev/null +++ b/asmx/src/org/objectweb/asm/xml/SAXCodeAdapter.java
@@ -0,0 +1,353 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004, 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 org.objectweb.asm.xml; + +import java.util.HashMap; +import java.util.Map; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.Handle; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.TypePath; +import org.objectweb.asm.util.AbstractVisitor; +import org.xml.sax.ContentHandler; +import org.xml.sax.helpers.AttributesImpl; + +/** + * A {@link MethodVisitor} that generates SAX 2.0 events from the visited + * method. + * + * @see org.objectweb.asm.xml.SAXClassAdapter + * @see org.objectweb.asm.xml.Processor + * + * @author Eugene Kuleshov + */ +public final class SAXCodeAdapter extends SAXAdapter implements MethodVisitor { + private Map labelNames; + + /** + * Constructs a new {@link SAXCodeAdapter SAXCodeAdapter} object. + * + * @param h content handler that will be used to send SAX 2.0 events. + * @param access + */ + public SAXCodeAdapter(ContentHandler h, int access) { + super(h); + labelNames = new HashMap(); + + if ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_NATIVE)) == 0) + { + addStart("code", new AttributesImpl()); + } + } + + public final void visitCode() { + } + + public final void visitInsn(int opcode) { + addElement(AbstractVisitor.OPCODES[opcode], new AttributesImpl()); + } + + public final void visitIntInsn(int opcode, int operand) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "value", "value", "", Integer.toString(operand)); + addElement(AbstractVisitor.OPCODES[opcode], attrs); + } + + public final void visitVarInsn(int opcode, int var) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "var", "var", "", Integer.toString(var)); + addElement(AbstractVisitor.OPCODES[opcode], attrs); + } + + public final void visitTypeInsn(int opcode, String desc) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "desc", "desc", "", desc); + addElement(AbstractVisitor.OPCODES[opcode], attrs); + } + + public final void visitFieldInsn( + int opcode, + String owner, + String name, + String desc) + { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "owner", "owner", "", owner); + attrs.addAttribute("", "name", "name", "", name); + attrs.addAttribute("", "desc", "desc", "", desc); + addElement(AbstractVisitor.OPCODES[opcode], attrs); + } + + public final void visitMethodInsn( + int opcode, + String owner, + String name, + String desc) + { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "owner", "owner", "", owner); + attrs.addAttribute("", "name", "name", "", name); + attrs.addAttribute("", "desc", "desc", "", desc); + addElement(AbstractVisitor.OPCODES[opcode], attrs); + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, + Object... bsmArgs) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "name", "name", "", name); + attrs.addAttribute("", "desc", "desc", "", desc); + attrs.addAttribute("", "bsm", "bsm", "", + SAXClassAdapter.encode(bsm.toString())); + String o = AbstractVisitor.OPCODES[Opcodes.INVOKEDYNAMIC]; + addStart(o, attrs); + for (int i = 0; i < bsmArgs.length; i++) { + AttributesImpl cattrs = new AttributesImpl(); + cattrs.addAttribute("", "cst", "cst", "", + SAXClassAdapter.encode(bsmArgs[i].toString())); + cattrs.addAttribute("", "desc", "desc", "", + Type.getDescriptor(bsmArgs[i].getClass())); + addElement("bsmArg", cattrs); + } + addEnd(o); + } + + public final void visitJumpInsn(int opcode, Label label) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "label", "label", "", getLabel(label)); + addElement(AbstractVisitor.OPCODES[opcode], attrs); + } + + public final void visitLabel(Label label) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "name", "name", "", getLabel(label)); + addElement("Label", attrs); + } + + public final void visitLdcInsn(Object cst) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", + "cst", + "cst", + "", + SAXClassAdapter.encode(cst.toString())); + attrs.addAttribute("", + "desc", + "desc", + "", + Type.getDescriptor(cst.getClass())); + addElement(AbstractVisitor.OPCODES[Opcodes.LDC], attrs); + } + + public final void visitIincInsn(int var, int increment) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "var", "var", "", Integer.toString(var)); + attrs.addAttribute("", "inc", "inc", "", Integer.toString(increment)); + addElement(AbstractVisitor.OPCODES[Opcodes.IINC], attrs); + } + + public final void visitTableSwitchInsn( + int min, + int max, + Label dflt, + Label[] labels) + { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "min", "min", "", Integer.toString(min)); + attrs.addAttribute("", "max", "max", "", Integer.toString(max)); + attrs.addAttribute("", "dflt", "dflt", "", getLabel(dflt)); + String o = AbstractVisitor.OPCODES[Opcodes.TABLESWITCH]; + addStart(o, attrs); + for (int i = 0; i < labels.length; i++) { + AttributesImpl att2 = new AttributesImpl(); + att2.addAttribute("", "name", "name", "", getLabel(labels[i])); + addElement("label", att2); + } + addEnd(o); + } + + public final void visitLookupSwitchInsn( + Label dflt, + int[] keys, + Label[] labels) + { + AttributesImpl att = new AttributesImpl(); + att.addAttribute("", "dflt", "dflt", "", getLabel(dflt)); + String o = AbstractVisitor.OPCODES[Opcodes.LOOKUPSWITCH]; + addStart(o, att); + for (int i = 0; i < labels.length; i++) { + AttributesImpl att2 = new AttributesImpl(); + att2.addAttribute("", "name", "name", "", getLabel(labels[i])); + att2.addAttribute("", "key", "key", "", Integer.toString(keys[i])); + addElement("label", att2); + } + addEnd(o); + } + + public final void visitMultiANewArrayInsn(String desc, int dims) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "desc", "desc", "", desc); + attrs.addAttribute("", "dims", "dims", "", Integer.toString(dims)); + addElement(AbstractVisitor.OPCODES[Opcodes.MULTIANEWARRAY], attrs); + } + + public final void visitTryCatchBlock( + Label start, + Label end, + Label handler, + String type) + { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "start", "start", "", getLabel(start)); + attrs.addAttribute("", "end", "end", "", getLabel(end)); + attrs.addAttribute("", "handler", "handler", "", getLabel(handler)); + if (type != null) + attrs.addAttribute("", "type", "type", "", type); + addElement("TryCatch", attrs); + } + + public final void visitMaxs(int maxStack, int maxLocals) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", + "maxStack", + "maxStack", + "", + Integer.toString(maxStack)); + attrs.addAttribute("", + "maxLocals", + "maxLocals", + "", + Integer.toString(maxLocals)); + addElement("Max", attrs); + + addEnd("code"); + } + + public void visitLocalVariable( + String name, + String desc, + String signature, + Label start, + Label end, + int index) + { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "name", "name", "", name); + attrs.addAttribute("", "desc", "desc", "", desc); + if (signature != null) + attrs.addAttribute("", + "signature", + "signature", + "", + SAXClassAdapter.encode(signature)); + attrs.addAttribute("", "start", "start", "", getLabel(start)); + attrs.addAttribute("", "end", "end", "", getLabel(end)); + attrs.addAttribute("", "var", "var", "", Integer.toString(index)); + addElement("LocalVar", attrs); + } + + public final void visitLineNumber(int line, Label start) { + AttributesImpl attrs = new AttributesImpl(); + attrs.addAttribute("", "line", "line", "", Integer.toString(line)); + attrs.addAttribute("", "start", "start", "", getLabel(start)); + addElement("LineNumber", attrs); + } + + public AnnotationVisitor visitAnnotationDefault() { + return new SAXAnnotationAdapter(getContentHandler(), + "annotationDefault", + 0, + null, + null); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new SAXAnnotationAdapter(getContentHandler(), + "annotation", + visible ? 1 : -1, + null, + desc); + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + return null; + } + //end jaime + + public AnnotationVisitor visitParameterAnnotation( + int parameter, + String desc, + boolean visible) + { + return new SAXAnnotationAdapter(getContentHandler(), + "parameterAnnotation", + visible ? 1 : -1, + parameter, + desc); + } + + public void visitEnd() { + addEnd("method"); + } + + public final void visitAttribute(Attribute attr) { + // TODO Auto-generated SAXCodeAdapter.visitAttribute + } + + private final String getLabel(Label label) { + String name = (String) labelNames.get(label); + if (name == null) { + name = Integer.toString(labelNames.size()); + labelNames.put(label, name); + } + return name; + } + + @Override + public AnnotationVisitor visitInsnAnnotation( + int typeRef, + TypePath typePath, + String desc, + boolean visible) + { + return null; + } + +}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXFieldAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXFieldAdapter.java new file mode 100644 index 0000000..195fd96 --- /dev/null +++ b/asmx/src/org/objectweb/asm/xml/SAXFieldAdapter.java
@@ -0,0 +1,87 @@ +/*** + * ASM XML Adapter + * Copyright (c) 2004, 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 org.objectweb.asm.xml; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +/** + * SAXFieldAdapter + * + * @author Eugene Kuleshov + */ +public class SAXFieldAdapter implements FieldVisitor { + private final ContentHandler h; + + public SAXFieldAdapter(ContentHandler h, AttributesImpl att) { + this.h = h; + + try { + h.startElement("", "field", "field", att); + } catch (SAXException ex) { + throw new RuntimeException(ex.toString()); + } + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return new SAXAnnotationAdapter(h, + "annotation", + visible ? 1 : -1, + null, + desc); + } + + // jaime + public TypeAnnotationVisitor visitTypeAnnotation(String desc, + boolean visible, + boolean inCode) + { + throw new RuntimeException("Jaime did not implement yet"); + } + //end jaime + + public void visitAttribute(Attribute attr) { + // TODO Auto-generated method stub + } + + public void visitEnd() { + try { + h.endElement("", "field", "field"); + } catch (SAXException ex) { + throw new RuntimeException(ex.toString()); + } + } + +}
diff --git a/asmx/test/README.txt b/asmx/test/README.txt new file mode 100644 index 0000000..f4d1e7e --- /dev/null +++ b/asmx/test/README.txt
@@ -0,0 +1,16 @@ +This directory contains the tests of the product. +It contains(*) the following items: + +- lib: external libraries required to run the tests +- conform: conformance tests (unit tests) +- deviance: deviance tests (unit tests) +- thread: multi-threading tests (unit tests) +- stress: stress tests +- perf: performance tests + +Each sub directory contains: +- the source of the tests, with package struture if there is one, +- the xml descriptors to launch test suites. An xml desriptor describes + the execution of a test suite. The default task is called on this file. + +(*) some items may not be present, depending on the product. \ No newline at end of file
diff --git a/asmx/test/build.xml b/asmx/test/build.xml new file mode 100644 index 0000000..0f92ec1 --- /dev/null +++ b/asmx/test/build.xml
@@ -0,0 +1,205 @@ +<!-- + ! ASM: a very small and fast Java bytecode manipulation framework + ! Copyright (c) 2000-2005 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. +--> + +<project name="test" default="test"> + + <!-- ==================================== --> + <!-- ======== PROPERTY DEFINITION ======= --> + <!-- ==================================== --> + + <property name="test.conform" value="${test}/conform"/> + <property name="test.deviance" value="${test}/deviance"/> + <property name="test.thread" value="${test}/thread"/> + <property name="test.stress" value="${test}/stress"/> + <property name="test.perf" value="${test}/perf"/> + + <target name="properties"> + <condition property="asm.test" value="${java.home}/lib/rt.jar,test/conform/cases"> + <not><isset property="asm.test"/></not> + </condition> + + <condition property="asm.test.class" value=""> + <not><isset property="asm.test.class"/></not> + </condition> + + <condition property="java5"> + <available classname="java.lang.annotation.Annotation"/> + </condition> + + <condition property="test.conform.exist"> + <available file="${test.conform}"/> + </condition> + + <condition property="test.deviance.exist"> + <available file="${test.deviance}"/> + </condition> + + <condition property="test.thread.exist"> + <available file="${test.thread}"/> + </condition> + + <condition property="test.stress.exist"> + <available file="${test.stress}"/> + </condition> + + <condition property="test.perf.exist"> + <available file="${test.perf}"/> + </condition> + + <condition property="test.all"> + <and> + <not><isset property="test.type"/></not> + <not><isset property="test.group"/></not> + <not><isset property="test.name"/></not> + </and> + </condition> + + <condition property="test.paths.configured"> + <and> + <isset property="bcel.path"/> + <isset property="serp.path"/> + <isset property="javassist.path"/> + <isset property="janino.path"/> + </and> + </condition> + </target> + + <!-- ================================== --> + <!-- ======== INITIALIZATION ======= --> + <!-- ================================== --> + + <target name="check" unless="test.paths.configured"> + <echo message="The 'build.properties' file must be configured"/> + <fail/> + </target> + + <target name="init" depends="properties,check"> + <mkdir dir="${out.test}"/> + <mkdir dir="${out.test}/reports"/> + <path id="test.classpath"> + <pathelement location="${classes}"/> + <pathelement location="${out.test}"/> + <pathelement path="${bcel.path}"/> + <pathelement path="${serp.path}"/> + <pathelement path="${javassist.path}"/> + <pathelement path="${janino.path}"/> + <path refid="cobertura.classpath"/> + </path> + </target> + + <!-- ==================================== --> + <!-- =========== COMPILATION ============ --> + <!-- ==================================== --> + + <target name="compile.test.conform" depends="init" if="test.conform.exist"> + <javac srcdir="${test.conform}" destdir="${out.test}" + debug="on" debuglevel="lines,vars,source"> + <classpath refid="test.classpath"/> + <include name="**/*.java"/> + <exclude name="annotations/**/*.java" unless="java5"/> + <exclude name="**/AnnotationTest.java" unless="java5"/> + </javac> + <copy todir="${out.test}"> + <fileset dir="${test.conform}"> + <include name="**/*.txt"/> + <include name="**/*.data"/> + </fileset> + </copy> + </target> + + <target name="compile.test.deviance" depends="init" if="test.deviance.exist"> + <javac srcdir="${test.deviance}" destdir="${out.test}" debug="on" source="1.3" target="1.2"> + <classpath refid="test.classpath"/> + <include name="**/*.java"/> + </javac> + </target> + + <target name="compile.test.thread" depends="init" if="test.thread.exist"> + <javac srcdir="${test.thread}" destdir="${out.test}" debug="on" source="1.3" target="1.2"> + <classpath refid="test.classpath"/> + <include name="**/*.java"/> + </javac> + </target> + + <target name="compile.test.stress" depends="init" if="test.stress.exist"> + <javac srcdir="${test.stress}" destdir="${out.test}" debug="on" source="1.3" target="1.2"> + <classpath refid="test.classpath"/> + <include name="**/*.java"/> + </javac> + </target> + + <target name="compile.test.perf" depends="init" if="test.perf.exist"> + <javac srcdir="${test.perf}" destdir="${out.test}" debug="on" source="1.3" target="1.2"> + <classpath refid="test.classpath"/> + <include name="**/*.java"/> + <exclude name="**/xml/*.java"/> + </javac> + </target> + + <target name="compile" depends="compile.test.conform,compile.test.deviance,compile.test.thread,compile.test.stress,compile.test.perf"/> + + <!-- ============================= --> + <!-- =========== TEST ============ --> + <!-- ============================= --> + + <target name="testAll" depends="compile" if="test.all"> + <multipleAnt dir="${test.conform}" inheritRefs="true"/> + <!--multipleAnt dir="${test.deviance}" inheritRefs="true"/> + <multipleAnt dir="${test.thread}" inheritRefs="true"/> + <multipleAnt dir="${test.stress}" inheritRefs="true"/--> + <multipleAnt dir="${test.perf}" inheritRefs="true"/> + </target> + + <target name="testType" depends="compile" if="test.type"> + <multipleAnt dir="${test}/${test.type}" inheritRefs="true"/> + </target> + + <target name="testGroup" depends="compile" if="test.group"> + <ant antfile="test/${test.group}.xml" inheritRefs="true"/> + </target> + + <target name="testName" depends="compile" if="test.name"> + <multipleAnt dir="${test.conform}" target="${test.name}" inheritRefs="true"/> + <!--multipleAnt dir="${test.deviance}" target="${test.name}" inheritRefs="true"/> + <multipleAnt dir="${test.thread}" target="${test.name}" inheritRefs="true"/> + <multipleAnt dir="${test.stress}" target="${test.name}" inheritRefs="true"/--> + <multipleAnt dir="${test.perf}" target="${test.name}" inheritRefs="true"/> + </target> + + <target name="test" depends="testAll,testType,testGroup,testName"> + <!--junitreport todir="${out.test}/reports"> + <fileset dir="${out.test}/reports"> + <include name="TEST-*.xml"/> + </fileset> + <report todir="${out.test}/reports"/> + </junitreport--> + </target> + +</project>
diff --git a/asmx/test/conform/adviceadapter.xml b/asmx/test/conform/adviceadapter.xml new file mode 100644 index 0000000..58d4ef7 --- /dev/null +++ b/asmx/test/conform/adviceadapter.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/AdviceAdapterTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/adviceadapter2.xml b/asmx/test/conform/adviceadapter2.xml new file mode 100644 index 0000000..fd0abcf --- /dev/null +++ b/asmx/test/conform/adviceadapter2.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/AdviceAdapterTest2.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/annotation.xml b/asmx/test/conform/annotation.xml new file mode 100644 index 0000000..ea76d1e --- /dev/null +++ b/asmx/test/conform/annotation.xml
@@ -0,0 +1,21 @@ +<project name="conform" default="test"> + + <target name="test" if="java5"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/AnnotationTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/annotations/ExtendedValues.class b/asmx/test/conform/annotations/ExtendedValues.class new file mode 100644 index 0000000..f229464 --- /dev/null +++ b/asmx/test/conform/annotations/ExtendedValues.class Binary files differ
diff --git a/asmx/test/conform/annotations/ExtendedValues.java b/asmx/test/conform/annotations/ExtendedValues.java new file mode 100644 index 0000000..f763ae5 --- /dev/null +++ b/asmx/test/conform/annotations/ExtendedValues.java
@@ -0,0 +1,82 @@ + +package annotations; + +import java.util.*; + +@ValuesAnnotation( + byteValue = 1, + charValue = 'A', + booleanValue = true, + intValue = 1, + shortValue = 1, + longValue = 1L, + floatValue = 1.0f, + doubleValue = 1.0d, + stringValue = "A", + + enumValue = ValuesEnum.ONE, + annotationValue = @ValueAttrAnnotation( "annotation"), + classValue = Values.class, + + byteArrayValue = { 1, -1}, + charArrayValue = { 'c', 'b', (char)-1}, + booleanArrayValue = {true, false}, + intArrayValue = { 1, -1}, + shortArrayValue = { (short)1, (short)-1}, + longArrayValue = { 1L, -1L}, + floatArrayValue = { 1.0f, -1.0f}, + doubleArrayValue = { 1.0d, -1.0d}, + stringArrayValue = { "aa", "bb"}, + + enumArrayValue = {ValuesEnum.ONE, ValuesEnum.TWO}, + annotationArrayValue = {@ValueAttrAnnotation( "annotation1"), @ValueAttrAnnotation( "annotation2")}, + classArrayValue = {Values.class, Values.class} +) +@ValueAttrAnnotation1( "classAnnotation1") +@ValueAttrAnnotation2( "classAnnotation2") +public class ExtendedValues { + + @ValueAttrAnnotation1( "fieldAnnotation1") + @ValueAttrAnnotation2( "fieldAnnotation2") + public String testfield = "test"; + + public Set<String> testSetOfString; + public Set<Map<String,String>> testSetOfMapOfStringString; + + @SuppressWarnings("unchecked") + @ValueAttrAnnotation1( "methodAnnotation1") + @ValueAttrAnnotation2( "methodAnnotation2") + @ValueAttrAnnotation() + public void testMethod( + @ValueAttrAnnotation1( "param1Annotation1") + @ValueAttrAnnotation2( "param1Annotation2") String param1, + @ValueAttrAnnotation1( "param2Annotation1") + @ValueAttrAnnotation2( "param2Annotation2") int param2) { + // @ValueAttrAnnotation( "codeAnnotation") + + Object o; + Integer testLocalVariable; + Map<String, Set<String>> testLocalMap; + + o = new Object(); + + testLocalMap = new HashMap<String, Set<String>>(); + + if(o instanceof Map) { + + } + + if(o instanceof Set[][]) { + + } + + testLocalVariable = (Integer) o; + testLocalMap = (HashMap<String, Set<String>>) o; + + } + + public Set<String> testMethod2() { + return null; + } + +}
diff --git a/asmx/test/conform/annotations/ExtendedValuesDump.class b/asmx/test/conform/annotations/ExtendedValuesDump.class new file mode 100644 index 0000000..e57afca --- /dev/null +++ b/asmx/test/conform/annotations/ExtendedValuesDump.class Binary files differ
diff --git a/asmx/test/conform/annotations/ExtendedValuesDump.java b/asmx/test/conform/annotations/ExtendedValuesDump.java new file mode 100644 index 0000000..3ff440f --- /dev/null +++ b/asmx/test/conform/annotations/ExtendedValuesDump.java
@@ -0,0 +1,3283 @@ +package annotations; +import org.objectweb.asm.*; +public class ExtendedValuesDump implements Opcodes { + + public static byte[] dump () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/Values", null, "java/lang/Object", null); + + cw.visitSource("Values.java", null); + + { + av0 = cw.visitAnnotation("Lannotations/ValuesAnnotation;", true); + av0.visit("byteValue", new Byte((byte)1)); + av0.visit("charValue", new Character((char)65)); + av0.visit("booleanValue", new Boolean(true)); + av0.visit("intValue", new Integer(1)); + av0.visit("shortValue", new Short((short)1)); + av0.visit("longValue", new Long(1L)); + av0.visit("floatValue", new Float("1.0")); + av0.visit("doubleValue", new Double("1.0")); + av0.visit("stringValue", "A"); + av0.visitEnum("enumValue", "Lannotations/ValuesEnum;", "ONE"); + { + AnnotationVisitor av1 = av0.visitAnnotation("annotationValue", "Lannotations/ValueAttrAnnotation;"); + av1.visit("value", "annotation"); + av1.visitEnd(); + } + av0.visit("classValue", Type.getType("Lannotations/Values;")); + av0.visit("byteArrayValue", new byte[] {1,-1}); + av0.visit("charArrayValue", new char[] {(char)99,(char)98,(char)65535}); + av0.visit("booleanArrayValue", new boolean[] {true,false}); + av0.visit("intArrayValue", new int[] {1,-1}); + av0.visit("shortArrayValue", new short[] {(short)1,(short)-1}); + av0.visit("longArrayValue", new long[] {1L,-1L}); + av0.visit("floatArrayValue", new float[] {1.0f,-1.0f}); + av0.visit("doubleArrayValue", new double[] {1.0d,-1.0d}); + { + AnnotationVisitor av1 = av0.visitArray("stringArrayValue"); + av1.visit(null, "aa"); + av1.visit(null, "bb"); + av1.visitEnd(); + } + { + AnnotationVisitor av1 = av0.visitArray("enumArrayValue"); + av1.visitEnum(null, "Lannotations/ValuesEnum;", "ONE"); + av1.visitEnum(null, "Lannotations/ValuesEnum;", "TWO"); + av1.visitEnd(); + } + { + AnnotationVisitor av1 = av0.visitArray("annotationArrayValue"); + { + AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;"); + av2.visit("value", "annotation1"); + av2.visitEnd(); + } + { + AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;"); + av2.visit("value", "annotation2"); + av2.visitEnd(); + } + av1.visitEnd(); + } + { + AnnotationVisitor av1 = av0.visitArray("classArrayValue"); + av1.visit(null, Type.getType("Lannotations/Values;")); + av1.visit(null, Type.getType("Lannotations/Values;")); + av1.visitEnd(); + } + av0.visitEnd(); + } + { + av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true); + av0.visit("value", "classAnnotation1"); + av0.visitEnd(); + } + { + av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true); + av0.visit("value", "classAnnotation2"); + av0.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "testfield", "Ljava/lang/String;", null, null); + { + av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true); + av0.visit("value", "fieldAnnotation1"); + av0.visitEnd(); + } + { + av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true); + av0.visit("value", "fieldAnnotation2"); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn("test"); + mv.visitFieldInsn(PUTFIELD, "annotations/Values", "testfield", "Ljava/lang/String;"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "testMethod", "(Ljava/lang/String;I)V", null, null); + { + av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true); + av0.visit("value", "methodAnnotation1"); + av0.visitEnd(); + } + { + av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true); + av0.visit("value", "methodAnnotation2"); + av0.visitEnd(); + } + { + av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation1;", true); + av0.visit("value", "param1Annotation1"); + av0.visitEnd(); + } + { + av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation2;", true); + av0.visit("value", "param1Annotation2"); + av0.visitEnd(); + } + { + av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation1;", true); + av0.visit("value", "param2Annotation1"); + av0.visitEnd(); + } + { + av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation2;", true); + av0.visit("value", "param2Annotation2"); + av0.visitEnd(); + } + mv.visitInsn(RETURN); + mv.visitMaxs(0, 3); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpClassEmpty () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestClassEmpty", null, "java/lang/Object", null); + + cw.visitSource("TestClassEmpty.java", null); + + { + av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(3, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassEmpty;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpClassNonEmpty () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestClassNonEmpty", null, "java/lang/Object", null); + + cw.visitSource("TestClassNonEmpty.java", null); + + { + av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "i", "I", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PRIVATE, "a", "Ljava/lang/String;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(7, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(8, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ICONST_0); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "i", "I"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(9, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l3, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PROTECTED, "<init>", "(Ljava/lang/String;)V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(11, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(12, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "a", "Ljava/lang/String;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(13, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l3, 0); + mv.visitLocalVariable("s", "Ljava/lang/String;", null, l0, l3, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "i", "()I", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(16, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "i", "I"); + mv.visitInsn(IRETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "a", "()Ljava/lang/String;", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(20, l0); + mv.visitTypeInsn(NEW, "java/lang/String"); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "a", "Ljava/lang/String;"); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "(Ljava/lang/String;)V"); + mv.visitVarInsn(ASTORE, 1); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(21, l1); + mv.visitTypeInsn(NEW, "java/lang/StringBuilder"); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;"); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V"); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"); + mv.visitVarInsn(ASTORE, 1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(22, l2); + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ARETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l3, 0); + mv.visitLocalVariable("s", "Ljava/lang/String;", null, l1, l3, 1); + mv.visitMaxs(3, 2); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + public static byte[] dumpFieldSimple () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestFieldSimple", null, "java/lang/Object", null); + + cw.visitSource("TestFieldSimple.java", null); + + { + av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "i", "I", null, null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PRIVATE, "j", "I", null, null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true); + av0.visit("value", "Hello"); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PROTECTED, "o", "Ljava/lang/Object;", null, null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true); + av0.visit("value", "H"); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(0, "s", "Ljava/lang/String;", null, null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true); + av0.visit("value", "E"); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(0, "f", "Lannotations/tests/classfile/cases/TestFieldSimple;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(3, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(7, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ACONST_NULL); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestFieldSimple", "s", "Ljava/lang/String;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(8, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ACONST_NULL); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestFieldSimple", "f", "Lannotations/tests/classfile/cases/TestFieldSimple;"); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(3, l3); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestFieldSimple;", null, l0, l4, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpFieldGeneric () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestFieldGeneric", "<T:Ljava/lang/Object;>Ljava/lang/Object;", "java/lang/Object", null); + + cw.visitSource("TestFieldGeneric.java", null); + + { + av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + fv = cw.visitField(0, "s", "Ljava/lang/String;", null, null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/F;", true); + av0.visit("fieldA", new Integer(1)); + av0.visit("fieldB", "fi"); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(0, "list", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/G;", true); + av0.visit("fieldA", new Integer(3)); + av0.visit("fieldB", "three"); + av0.visit("fieldC", new boolean[] {true,false}); + av0.visit("fieldD", new Integer(2)); + av0.visit("fieldE", new Integer(4)); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(0, "set", "Ljava/util/Set;", "Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric;>;", null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/E;", true); + av0.visit("fieldA", new Integer(2)); + av0.visit("fieldB", "rh"); + av0.visitEnd(); + } + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/F;", true); + av0.visit("fieldA", new Integer(1)); + av0.visit("fieldB", "if"); + av0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(0, "testFieldGeneric", "Lannotations/tests/classfile/cases/TestFieldGeneric;", "Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;", null); + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(0, "otherSet", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null); + fv.visitEnd(); + } + { + fv = cw.visitField(0, "nestedSet", "Ljava/util/Set;", "Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric<Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric;>;>;>;", null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true); + av0.visit("value", "nested"); + av0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/F;", true); + xav0.visit("fieldA", new Integer(1)); + xav0.visit("fieldB", "n"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(-2)); + xav0.visit("fieldB", "nl"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "nil"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/D;", true); + xav0.visit("fieldA", new Integer(-1)); + xav0.visit("fieldB", "hello"); + xav0.visit("fieldC", new int[] {3,2,4}); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + fv.visitEnd(); + } + { + fv = cw.visitField(0, "nestedMap", "Ljava/util/Map;", "Ljava/util/Map<Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric;>;Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;>;", null); + { + av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/C;", true); + av0.visit("fieldA", new Integer(1)); + av0.visit("fieldB", "nested"); + av0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "inner most T"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(256)); + xav0.visit("fieldB", "hello"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "inner most F"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(13, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(11, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "annotations/tests/classfile/cases/TestFieldGeneric"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestFieldGeneric", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestFieldGeneric", "testFieldGeneric", "Lannotations/tests/classfile/cases/TestFieldGeneric;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(15, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestFieldGeneric;", "Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;", l0, l3, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(20, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestFieldGeneric", "s", "Ljava/lang/String;"); + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestFieldGeneric;", "Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;", l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + public static byte[] dumpLocalVariable () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestLocalVariable", "<T:Ljava/lang/Object;>Ljava/lang/Object;", "java/lang/Object", null); + + cw.visitSource("TestLocalVariable.java", null); + + { + av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "i", "I", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/util/Set;>;", null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(166)); + xav0.visit("fieldB", "good"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(6)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(10, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(11, l1); + mv.visitInsn(ICONST_0); + mv.visitVarInsn(ISTORE, 1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(12, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(ICONST_0); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I"); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(13, l3); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l4, 0); + mv.visitLocalVariable("t", "I", null, l2, l4, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(I)V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(15, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(16, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 1); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(17, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l3, 0); + mv.visitLocalVariable("i", "I", null, l0, l3, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Integer;)V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(19, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(20, l1); + mv.visitInsn(ICONST_1); + mv.visitVarInsn(ISTORE, 2); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(21, l2); + mv.visitIincInsn(2, 1); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(22, l3); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I"); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLineNumber(23, l4); + mv.visitIincInsn(2, -1); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLineNumber(24, l5); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 2); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I"); + Label l6 = new Label(); + mv.visitLabel(l6); + mv.visitLineNumber(25, l6); + mv.visitInsn(RETURN); + Label l7 = new Label(); + mv.visitLabel(l7); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l7, 0); + mv.visitLocalVariable("j", "Ljava/lang/Integer;", null, l0, l7, 1); + mv.visitLocalVariable("k", "I", null, l2, l7, 2); + mv.visitMaxs(2, 3); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "i", "()I", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(28, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I"); + mv.visitInsn(IRETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "j", "()I", null, null); + { + av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "hello"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(2)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(32, l0); + mv.visitInsn(ICONST_1); + mv.visitVarInsn(ISTORE, 1); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(33, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, "annotations/tests/classfile/cases/TestLocalVariable", "j", "()I"); + mv.visitInsn(IRETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l2, 0); + mv.visitLocalVariable("temp", "I", null, l1, l2, 1); + mv.visitMaxs(1, 2); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "someMethod", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(37, l0); + mv.visitTypeInsn(NEW, "annotations/tests/classfile/cases/TestLocalVariable"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestLocalVariable", "<init>", "()V"); + mv.visitVarInsn(ASTORE, 0); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(38, l1); + mv.visitTypeInsn(NEW, "java/lang/String"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "()V"); + mv.visitVarInsn(ASTORE, 1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(39, l2); + mv.visitLdcInsn(new Double("2.0")); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;"); + mv.visitVarInsn(ASTORE, 2); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(40, l3); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("t", "Lannotations/tests/classfile/cases/TestLocalVariable;", null, l1, l4, 0); + mv.visitLocalVariable("s", "Ljava/lang/String;", null, l2, l4, 1); + mv.visitLocalVariable("d", "Ljava/lang/Double;", null, l3, l4, 2); + mv.visitMaxs(2, 3); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(43, l0); + mv.visitInsn(ICONST_1); + mv.visitVarInsn(ISTORE, 1); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(44, l1); + mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); + mv.visitVarInsn(ISTORE, 2); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(45, l2); + mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); + mv.visitVarInsn(ISTORE, 3); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(46, l3); + mv.visitVarInsn(ILOAD, 2); + Label l4 = new Label(); + mv.visitJumpInsn(IFEQ, l4); + mv.visitVarInsn(ILOAD, 3); + mv.visitJumpInsn(IFEQ, l4); + mv.visitInsn(ICONST_1); + Label l5 = new Label(); + mv.visitJumpInsn(GOTO, l5); + mv.visitLabel(l4); + mv.visitInsn(ICONST_0); + mv.visitLabel(l5); + mv.visitVarInsn(ISTORE, 1); + Label l6 = new Label(); + mv.visitLabel(l6); + mv.visitLineNumber(47, l6); + mv.visitVarInsn(ILOAD, 1); + Label l7 = new Label(); + mv.visitJumpInsn(IFNE, l7); + mv.visitVarInsn(ILOAD, 3); + Label l8 = new Label(); + mv.visitJumpInsn(IFEQ, l8); + mv.visitLabel(l7); + mv.visitLineNumber(48, l7); + mv.visitVarInsn(ILOAD, 1); + mv.visitVarInsn(ISTORE, 2); + mv.visitLabel(l8); + mv.visitLineNumber(50, l8); + mv.visitVarInsn(ILOAD, 2); + Label l9 = new Label(); + mv.visitJumpInsn(IFEQ, l9); + Label l10 = new Label(); + mv.visitLabel(l10); + mv.visitLineNumber(51, l10); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("Message"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + mv.visitLabel(l9); + mv.visitLineNumber(53, l9); + mv.visitInsn(RETURN); + Label l11 = new Label(); + mv.visitLabel(l11); + mv.visitLocalVariable("args", "[Ljava/lang/String;", null, l0, l11, 0); + mv.visitLocalVariable("b", "Z", null, l1, l11, 1); + mv.visitLocalVariable("b1", "Z", null, l2, l11, 2); + mv.visitLocalVariable("b2", "Z", null, l3, l11, 3); + mv.visitMaxs(2, 4); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpLocalVariableGenericArray () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", null, "java/lang/Object", null); + + cw.visitSource("TestLocalVariableGenericArray.java", null); + + { + fv = cw.visitField(0, "i", "Ljava/lang/Integer;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(0, "map1", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;", null); + fv.visitEnd(); + } + { + fv = cw.visitField(0, "map2", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/ArrayList<Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;>;>;", null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "good"); + xav0.visitXTargetType(new Integer(8)); + xav0.visitXStartPc(new Integer(37)); + xav0.visitXLength(new Integer(55)); + xav0.visitXIndex(new Integer(2)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "first param"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(37)); + xav0.visitXLength(new Integer(55)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "second param"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(37)); + xav0.visitXLength(new Integer(55)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(17, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(18, l1); + mv.visitInsn(ICONST_1); + mv.visitVarInsn(ISTORE, 1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(19, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "map2", "Ljava/util/Map;"); + mv.visitLdcInsn("4gf"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/ArrayList"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "iterator", "()Ljava/util/Iterator;"); + mv.visitVarInsn(ASTORE, 3); + Label l3 = new Label(); + mv.visitJumpInsn(GOTO, l3); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Map"); + mv.visitVarInsn(ASTORE, 2); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLineNumber(20, l5); + mv.visitVarInsn(ILOAD, 1); + mv.visitInsn(ICONST_5); + Label l6 = new Label(); + mv.visitJumpInsn(IF_ICMPGE, l6); + Label l7 = new Label(); + mv.visitLabel(l7); + mv.visitLineNumber(21, l7); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "map2", "Ljava/util/Map;"); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/ArrayList"); + mv.visitTypeInsn(NEW, "java/util/ArrayList"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "indexOf", "(Ljava/lang/Object;)I"); + mv.visitVarInsn(ISTORE, 1); + Label l8 = new Label(); + mv.visitJumpInsn(GOTO, l8); + mv.visitLabel(l6); + mv.visitLineNumber(23, l6); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "i", "Ljava/lang/Integer;"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); + mv.visitInsn(ICONST_5); + mv.visitInsn(IADD); + mv.visitVarInsn(ISTORE, 1); + mv.visitLabel(l8); + mv.visitLineNumber(25, l8); + mv.visitIincInsn(1, 1); + mv.visitLabel(l3); + mv.visitLineNumber(19, l3); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z"); + mv.visitJumpInsn(IFNE, l4); + Label l9 = new Label(); + mv.visitLabel(l9); + mv.visitLineNumber(27, l9); + mv.visitInsn(RETURN); + Label l10 = new Label(); + mv.visitLabel(l10); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l10, 0); + mv.visitLocalVariable("k", "I", null, l2, l10, 1); + mv.visitLocalVariable("e", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;", l5, l9, 2); + mv.visitMaxs(3, 4); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "someMethod", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "bad"); + xav0.visitXTargetType(new Integer(8)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(26)); + xav0.visitXIndex(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(0)); + xav0.visit("fieldB", "String"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(26)); + xav0.visitXIndex(new Integer(1)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(30, l0); + mv.visitTypeInsn(NEW, "java/util/HashSet"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V"); + mv.visitVarInsn(ASTORE, 1); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(31, l1); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(NEW, "java/lang/String"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "()V"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add", "(Ljava/lang/Object;)Z"); + mv.visitInsn(POP); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(32, l2); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add", "(Ljava/lang/Object;)Z"); + mv.visitInsn(POP); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(33, l3); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l4, 0); + mv.visitLocalVariable("s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", l1, l4, 1); + mv.visitMaxs(3, 2); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PRIVATE, "someMethod2", "(I)I", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(0)); + xav0.visit("fieldB", "Boolean"); + xav0.visitXTargetType(new Integer(8)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(66)); + xav0.visitXIndex(new Integer(2)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(66)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(8)); + xav0.visitXStartPc(new Integer(16)); + xav0.visitXLength(new Integer(58)); + xav0.visitXIndex(new Integer(3)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "inner-type"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(16)); + xav0.visitXLength(new Integer(58)); + xav0.visitXIndex(new Integer(3)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(36, l0); + mv.visitTypeInsn(NEW, "java/util/HashSet"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V"); + mv.visitVarInsn(ASTORE, 2); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(37, l1); + mv.visitTypeInsn(NEW, "java/util/HashSet"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V"); + mv.visitVarInsn(ASTORE, 3); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(38, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "someMethod3", "()Z"); + mv.visitVarInsn(ISTORE, 4); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(39, l3); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); + mv.visitVarInsn(ILOAD, 4); + mv.visitInsn(IAND); + Label l4 = new Label(); + mv.visitJumpInsn(IFEQ, l4); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLineNumber(40, l5); + mv.visitVarInsn(ILOAD, 4); + Label l6 = new Label(); + mv.visitJumpInsn(IFEQ, l6); + mv.visitVarInsn(ILOAD, 1); + Label l7 = new Label(); + mv.visitJumpInsn(GOTO, l7); + mv.visitLabel(l6); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); + mv.visitLabel(l7); + mv.visitInsn(IRETURN); + mv.visitLabel(l4); + mv.visitLineNumber(42, l4); + mv.visitVarInsn(ILOAD, 1); + mv.visitInsn(IRETURN); + Label l8 = new Label(); + mv.visitLabel(l8); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l8, 0); + mv.visitLocalVariable("i", "I", null, l0, l8, 1); + mv.visitLocalVariable("s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/Boolean;>;", l1, l8, 2); + mv.visitLocalVariable("ints", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/Integer;>;", l2, l8, 3); + mv.visitLocalVariable("b", "Z", null, l3, l8, 4); + mv.visitMaxs(2, 5); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PRIVATE, "someMethod3", "()Z", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "t"); + xav0.visitXTargetType(new Integer(8)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(70)); + xav0.visitXIndex(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "map key string"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(70)); + xav0.visitXIndex(new Integer(1)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "map value set"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(70)); + xav0.visitXIndex(new Integer(1)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(70)); + xav0.visitXIndex(new Integer(1)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(10)); + xav0.visitXLength(new Integer(68)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(1)); + xav0.visit("fieldB", "set of maps"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(10)); + xav0.visitXLength(new Integer(68)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "maps"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(10)); + xav0.visitXLength(new Integer(68)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "map key is integer"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(10)); + xav0.visitXLength(new Integer(68)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "map value is 2-d array"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(10)); + xav0.visitXLength(new Integer(68)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "first dimension"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(10)); + xav0.visitXLength(new Integer(68)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(4)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "second dimension"); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(10)); + xav0.visitXLength(new Integer(68)); + xav0.visitXIndex(new Integer(2)); + xav0.visitXLocationLength(new Integer(4)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(46, l0); + mv.visitTypeInsn(NEW, "java/util/HashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V"); + mv.visitVarInsn(ASTORE, 1); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(47, l1); + mv.visitInsn(ACONST_NULL); + mv.visitVarInsn(ASTORE, 2); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(49, l2); + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn("3"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Set"); + mv.visitTypeInsn(NEW, "java/util/HashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add", "(Ljava/lang/Object;)Z"); + mv.visitInsn(POP); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(51, l3); + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn("4"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Set"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Map"); + mv.visitInsn(ICONST_3); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "[[Ljava/lang/String;"); + mv.visitInsn(ICONST_2); + mv.visitInsn(AALOAD); + mv.visitInsn(ICONST_4); + mv.visitLdcInsn("Hello"); + mv.visitInsn(AASTORE); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLineNumber(53, l4); + mv.visitInsn(ICONST_1); + mv.visitInsn(IRETURN); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l5, 0); + mv.visitLocalVariable("t", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;", l1, l5, 1); + mv.visitLocalVariable("s", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/util/Map<Ljava/lang/Integer;[[Ljava/lang/String;>;>;>;", l2, l5, 2); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PROTECTED, "someMethod4", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(9)); + xav0.visitXStartPc(new Integer(8)); + xav0.visitXLength(new Integer(10)); + xav0.visitXIndex(new Integer(1)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + Label l1 = new Label(); + mv.visitTryCatchBlock(l0, l1, l1, "java/lang/Exception"); + mv.visitLabel(l0); + mv.visitLineNumber(58, l0); + mv.visitTypeInsn(NEW, "java/util/HashSet"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V"); + mv.visitVarInsn(ASTORE, 1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(60, l2); + mv.visitTypeInsn(NEW, "java/lang/RuntimeException"); + mv.visitInsn(DUP); + mv.visitLdcInsn("Hello"); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V"); + mv.visitInsn(ATHROW); + mv.visitLabel(l1); + mv.visitLineNumber(61, l1); + mv.visitVarInsn(ASTORE, 1); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(62, l3); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "i", "Ljava/lang/Integer;"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V"); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLineNumber(64, l4); + mv.visitInsn(RETURN); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l5, 0); + mv.visitLocalVariable("s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", l2, l1, 1); + mv.visitLocalVariable("e", "Ljava/lang/Exception;", null, l3, l4, 1); + mv.visitMaxs(3, 2); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + public static byte[] dumpMethodReceiver () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestMethodReceiver", null, "java/lang/Object", null); + + cw.visitSource("TestMethodReceiver.java", null); + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(9, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(6)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "first method"); + xav0.visitXTargetType(new Integer(6)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(12, l0); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("test()"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(13, l1); + mv.visitInsn(RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PRIVATE, "test2", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(2)); + xav0.visit("fieldB", "rec"); + xav0.visitXTargetType(new Integer(6)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(16, l0); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("test2()"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(17, l1); + mv.visitInsn(RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PROTECTED, "test3", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(6)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(20, l0); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("test3()"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(21, l1); + mv.visitInsn(RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(0, "test4", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "last method"); + xav0.visitXTargetType(new Integer(6)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(24, l0); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("test4()"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(25, l1); + mv.visitInsn(RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpMethodReturnTypeGenericArray () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray", null, "java/lang/Object", null); + + cw.visitSource("TestMethodReturnTypeGenericArray.java", null); + + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(7, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "()Ljava/util/List;", null, null); + { + av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(10, l0); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test2", "()Ljava/util/List;", "()Ljava/util/List<Ljava/lang/String;>;", null); + { + av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true); + av0.visit("value", "single-depth"); + av0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(14, l0); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test3", "()[Ljava/lang/String;", null, null); + { + av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "on array element"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(18, l0); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test4", "()[[Ljava/lang/String;", null, null); + { + av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true); + av0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "on"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "in"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(22, l0); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test5", "()Ljava/util/Set;", "()Ljava/util/Set<[Ljava/lang/String;>;", null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "two-deep"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(26, l0); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test6", "()Ljava/util/Map;", "()Ljava/util/Map<Ljava/util/Map<[Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;Ljava/util/Set<[Ljava/lang/String;>;>;", null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "map as key"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "array of value"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "inner-most value"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "set as value"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "innermost key or key"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(1)); + xav0.visit("fieldB", "value of key"); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(11)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(30, l0); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpObjectCreation () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestObjectCreation", null, "java/lang/Object", null); + + cw.visitSource("TestObjectCreation.java", null); + + { + fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(7, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "first new"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "a string"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(12)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(23)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(11, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(12, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/lang/String"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(13, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/lang/String"); + mv.visitInsn(DUP); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "(Ljava/lang/String;)V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(14, l3); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l4, 0); + mv.visitMaxs(4, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(7)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(14)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(17, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn("str"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(18, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/util/ArrayList"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(19, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l3, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "new"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(12)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(22, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/util/HashSet"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(23, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/util/HashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(24, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l3, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "self test"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(13)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(27, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/lang/Integer"); + mv.visitInsn(DUP); + mv.visitInsn(ICONST_2); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(28, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "annotations/tests/classfile/cases/TestObjectCreation"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestObjectCreation", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(29, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l3, 0); + mv.visitMaxs(4, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpObjectCreationGenericArray () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", null, "java/lang/Object", null); + + cw.visitSource("TestObjectCreationGenericArray.java", null); + + { + fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(9, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "first new"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(3)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(3)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(13, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitIntInsn(BIPUSH, 10); + mv.visitIntInsn(NEWARRAY, T_INT); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(14, l1); + mv.visitInsn(RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l2, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(23)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "str"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(23)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(17, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn("str"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(18, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/util/ArrayList"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(19, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l3, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "new"); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "map"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "map key string"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(12)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "first level"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(12)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "value"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(12)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "on the array"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(12)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "on array elements"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(12)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(22, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/util/HashSet"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(23, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/util/HashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(24, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l3, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(4)); + xav0.visitXOffset(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "key"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "value"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "key element"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "value array"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "value array element"); + xav0.visitXTargetType(new Integer(5)); + xav0.visitXOffset(new Integer(1)); + xav0.visitXLocationLength(new Integer(3)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(27, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/util/HashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(28, l1); + mv.visitInsn(RETURN); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l2, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpTypecast () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestTypecast", null, "java/lang/Object", null); + + cw.visitSource("TestTypecast.java", null); + + { + fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "s", "Ljava/lang/String;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "i", "Ljava/lang/Integer;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "b", "Ljava/lang/Boolean;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "set", "Ljava/util/Set;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "hset", "Ljava/util/HashSet;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "map", "Ljava/util/Map;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(7, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecast;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(21)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "second cast"); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(32)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(3)); + xav0.visit("fieldB", "cast"); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(59)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(4)); + xav0.visit("fieldB", "cast"); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(70)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(17, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(18, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "s", "Ljava/lang/String;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(19, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/lang/String"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "s", "Ljava/lang/String;"); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(20, l3); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "i", "Ljava/lang/Integer;"); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLineNumber(21, l4); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "b", "Ljava/lang/Boolean;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "b", "Ljava/lang/Boolean;"); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLineNumber(22, l5); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "hset", "Ljava/util/HashSet;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "set", "Ljava/util/Set;"); + Label l6 = new Label(); + mv.visitLabel(l6); + mv.visitLineNumber(23, l6); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "set", "Ljava/util/Set;"); + mv.visitTypeInsn(CHECKCAST, "java/util/HashSet"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "hset", "Ljava/util/HashSet;"); + Label l7 = new Label(); + mv.visitLabel(l7); + mv.visitLineNumber(24, l7); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "hset", "Ljava/util/HashSet;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Map"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "map", "Ljava/util/Map;"); + Label l8 = new Label(); + mv.visitLabel(l8); + mv.visitLineNumber(25, l8); + mv.visitInsn(ICONST_0); + mv.visitVarInsn(ISTORE, 1); + Label l9 = new Label(); + mv.visitLabel(l9); + mv.visitLineNumber(26, l9); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 1); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "i", "Ljava/lang/Integer;"); + Label l10 = new Label(); + mv.visitLabel(l10); + mv.visitLineNumber(27, l10); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 1); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;"); + Label l11 = new Label(); + mv.visitLabel(l11); + mv.visitLineNumber(28, l11); + mv.visitInsn(RETURN); + Label l12 = new Label(); + mv.visitLabel(l12); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecast;", null, l0, l12, 0); + mv.visitLocalVariable("pi", "I", null, l9, l12, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + public static byte[] dumpTypecastGenericArray () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestTypecastGenericArray", null, "java/lang/Object", null); + + cw.visitSource("TestTypecastGenericArray.java", null); + + { + fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "s", "Ljava/lang/String;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "i", "Ljava/lang/Integer;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "b", "Ljava/lang/Boolean;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "set", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "hset", "Ljava/util/HashSet;", "Ljava/util/HashSet<Ljava/util/Set<Ljava/lang/String;>;>;", null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PUBLIC, "map", "Ljava/util/Map;", "Ljava/util/Map<Ljava/util/Set<Ljava/lang/String;>;Ljava/util/Set<Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;>;>;", null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(8, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(21)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "second"); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(32)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(18, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(19, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "s", "Ljava/lang/String;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(20, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/lang/String"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "s", "Ljava/lang/String;"); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(21, l3); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "i", "Ljava/lang/Integer;"); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLineNumber(22, l4); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "b", "Ljava/lang/Boolean;"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "b", "Ljava/lang/Boolean;"); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLineNumber(23, l5); + mv.visitInsn(RETURN); + Label l6 = new Label(); + mv.visitLabel(l6); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l6, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(5)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "B"); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(16)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true); + xav0.visit("fieldA", new Integer(2)); + xav0.visit("fieldB", ""); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(16)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(27, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/HashSet"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(28, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Set"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(29, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l3, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(20)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "v"); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(23)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(33, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "map", "Ljava/util/Map;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "keySet", "()Ljava/util/Set;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/HashSet"); + mv.visitTypeInsn(CHECKCAST, "java/util/HashSet"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(34, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/HashSet"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "hset", "Ljava/util/HashSet;"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(35, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l3, 0); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(5)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "second"); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(5)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(15)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(15)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "set"); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(30)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(43)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "on the set"); + xav0.visitXTargetType(new Integer(0)); + xav0.visitXOffset(new Integer(53)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "on value"); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(53)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(39, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Map"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "map", "Ljava/util/Map;"); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(40, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Set"); + mv.visitVarInsn(ASTORE, 1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(41, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "map", "Ljava/util/Map;"); + mv.visitInsn(ACONST_NULL); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Set"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Map"); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Set"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;"); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(42, l3); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l4, 0); + mv.visitLocalVariable("t", "Ljava/util/Set;", "Ljava/util/Set<Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;>;", l2, l4, 1); + mv.visitMaxs(3, 2); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test5", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "string is key"); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(4)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "2d-array is value"); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(4)); + xav0.visitXLocationLength(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "first dimension"); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(4)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(0)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "second dimension"); + xav0.visitXTargetType(new Integer(1)); + xav0.visitXOffset(new Integer(4)); + xav0.visitXLocationLength(new Integer(2)); + xav0.visitXLocation(new Integer(1)); + xav0.visitXLocation(new Integer(1)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(47, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(CHECKCAST, "java/util/Map"); + mv.visitVarInsn(ASTORE, 1); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(48, l1); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V"); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(49, l2); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l3, 0); + mv.visitLocalVariable("m", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;[[Ljava/lang/String;>;", l1, l3, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } + + public static byte[] dumpTypeTest () throws Exception { + + ClassWriter cw = new ClassWriter(false); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + TypeAnnotationVisitor xav0; + + cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestTypeTest", null, "java/lang/Object", null); + + cw.visitSource("TestTypeTest.java", null); + + { + fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$0", "Ljava/lang/Class;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$1", "Ljava/lang/Class;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$2", "Ljava/lang/Class;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$3", "Ljava/lang/Class;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$4", "Ljava/lang/Class;", null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$5", "Ljava/lang/Class;", null, null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(8, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l1, 0); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "ismap"); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(4)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(14)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "islist"); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(24)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(12, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/util/Map"); + Label l1 = new Label(); + mv.visitJumpInsn(IFEQ, l1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(13, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/util/Set"); + mv.visitJumpInsn(IFEQ, l1); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(14, l3); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/util/List"); + mv.visitJumpInsn(IFEQ, l1); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLineNumber(15, l4); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitLabel(l1); + mv.visitLineNumber(19, l1); + mv.visitInsn(RETURN); + Label l5 = new Label(); + mv.visitLabel(l5); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l5, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(4)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(14)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(22, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/util/List"); + Label l1 = new Label(); + mv.visitJumpInsn(IFEQ, l1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(23, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/util/ArrayList"); + mv.visitJumpInsn(IFEQ, l1); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(24, l3); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitLabel(l1); + mv.visitLineNumber(27, l1); + mv.visitInsn(RETURN); + Label l4 = new Label(); + mv.visitLabel(l4); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l4, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "instanceof object"); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(4)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(30, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/lang/Object"); + Label l1 = new Label(); + mv.visitJumpInsn(IFNE, l1); + Label l2 = new Label(); + mv.visitLabel(l2); + mv.visitLineNumber(31, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitLabel(l1); + mv.visitLineNumber(33, l1); + mv.visitInsn(RETURN); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l3, 0); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null); + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(12)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "second"); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(28)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(44)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true); + xav0.visit("value", "fourth"); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(60)); + xav0.visitEnd(); + } + { + xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true); + xav0.visitXTargetType(new Integer(2)); + xav0.visitXOffset(new Integer(76)); + xav0.visitEnd(); + } + mv.visitCode(); + Label l0 = new Label(); + mv.visitLabel(l0); + mv.visitLineNumber(36, l0); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;"); + mv.visitVarInsn(ASTORE, 1); + Label l1 = new Label(); + mv.visitLabel(l1); + mv.visitLineNumber(37, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/lang/Boolean"); + Label l2 = new Label(); + mv.visitJumpInsn(IFEQ, l2); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitLineNumber(38, l3); + mv.visitLdcInsn(Type.getType("Ljava/lang/Boolean;")); + mv.visitVarInsn(ASTORE, 1); + Label l4 = new Label(); + mv.visitJumpInsn(GOTO, l4); + mv.visitLabel(l2); + mv.visitLineNumber(39, l2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/lang/Integer"); + Label l5 = new Label(); + mv.visitJumpInsn(IFEQ, l5); + Label l6 = new Label(); + mv.visitLabel(l6); + mv.visitLineNumber(40, l6); + mv.visitLdcInsn(Type.getType("Ljava/lang/Integer;")); + mv.visitVarInsn(ASTORE, 1); + mv.visitJumpInsn(GOTO, l4); + mv.visitLabel(l5); + mv.visitLineNumber(41, l5); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/lang/Character"); + Label l7 = new Label(); + mv.visitJumpInsn(IFEQ, l7); + Label l8 = new Label(); + mv.visitLabel(l8); + mv.visitLineNumber(42, l8); + mv.visitLdcInsn(Type.getType("Ljava/lang/Character;")); + mv.visitVarInsn(ASTORE, 1); + mv.visitJumpInsn(GOTO, l4); + mv.visitLabel(l7); + mv.visitLineNumber(43, l7); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/lang/String"); + Label l9 = new Label(); + mv.visitJumpInsn(IFEQ, l9); + Label l10 = new Label(); + mv.visitLabel(l10); + mv.visitLineNumber(44, l10); + mv.visitLdcInsn(Type.getType("Ljava/lang/String;")); + mv.visitVarInsn(ASTORE, 1); + mv.visitJumpInsn(GOTO, l4); + mv.visitLabel(l9); + mv.visitLineNumber(45, l9); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;"); + mv.visitTypeInsn(INSTANCEOF, "java/util/List"); + Label l11 = new Label(); + mv.visitJumpInsn(IFEQ, l11); + Label l12 = new Label(); + mv.visitLabel(l12); + mv.visitLineNumber(46, l12); + mv.visitLdcInsn(Type.getType("Ljava/util/List;")); + mv.visitVarInsn(ASTORE, 1); + mv.visitJumpInsn(GOTO, l4); + mv.visitLabel(l11); + mv.visitLineNumber(48, l11); + mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); + mv.visitVarInsn(ASTORE, 1); + mv.visitLabel(l4); + mv.visitLineNumber(50, l4); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V"); + Label l13 = new Label(); + mv.visitLabel(l13); + mv.visitLineNumber(51, l13); + mv.visitInsn(RETURN); + Label l14 = new Label(); + mv.visitLabel(l14); + mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l14, 0); + mv.visitLocalVariable("c", "Ljava/lang/Class;", null, l1, l14, 1); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + + return cw.toByteArray(); + } +}
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation.class b/asmx/test/conform/annotations/ValueAttrAnnotation.class new file mode 100644 index 0000000..90795d8 --- /dev/null +++ b/asmx/test/conform/annotations/ValueAttrAnnotation.class Binary files differ
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation.java b/asmx/test/conform/annotations/ValueAttrAnnotation.java new file mode 100644 index 0000000..843cf44 --- /dev/null +++ b/asmx/test/conform/annotations/ValueAttrAnnotation.java
@@ -0,0 +1,12 @@ + +package annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +@Retention(RetentionPolicy.RUNTIME) +public @interface ValueAttrAnnotation { + String value() default "defaultValue"; +} +
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation1.class b/asmx/test/conform/annotations/ValueAttrAnnotation1.class new file mode 100644 index 0000000..dfbd274 --- /dev/null +++ b/asmx/test/conform/annotations/ValueAttrAnnotation1.class Binary files differ
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation1.java b/asmx/test/conform/annotations/ValueAttrAnnotation1.java new file mode 100644 index 0000000..d6dbd81 --- /dev/null +++ b/asmx/test/conform/annotations/ValueAttrAnnotation1.java
@@ -0,0 +1,12 @@ + +package annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +@Retention(RetentionPolicy.RUNTIME) +public @interface ValueAttrAnnotation1 { + String value(); +} +
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation2.class b/asmx/test/conform/annotations/ValueAttrAnnotation2.class new file mode 100644 index 0000000..3c46fd2 --- /dev/null +++ b/asmx/test/conform/annotations/ValueAttrAnnotation2.class Binary files differ
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation2.java b/asmx/test/conform/annotations/ValueAttrAnnotation2.java new file mode 100644 index 0000000..27bed02 --- /dev/null +++ b/asmx/test/conform/annotations/ValueAttrAnnotation2.java
@@ -0,0 +1,12 @@ + +package annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +@Retention(RetentionPolicy.RUNTIME) +public @interface ValueAttrAnnotation2 { + String value(); +} +
diff --git a/asmx/test/conform/annotations/Values.class b/asmx/test/conform/annotations/Values.class new file mode 100644 index 0000000..f2b5881 --- /dev/null +++ b/asmx/test/conform/annotations/Values.class Binary files differ
diff --git a/asmx/test/conform/annotations/Values.java b/asmx/test/conform/annotations/Values.java new file mode 100644 index 0000000..2c30a30 --- /dev/null +++ b/asmx/test/conform/annotations/Values.java
@@ -0,0 +1,54 @@ + +package annotations; + + +@ValuesAnnotation( + byteValue = 1, + charValue = 'A', + booleanValue = true, + intValue = 1, + shortValue = 1, + longValue = 1L, + floatValue = 1.0f, + doubleValue = 1.0d, + stringValue = "A", + + enumValue = ValuesEnum.ONE, + annotationValue = @ValueAttrAnnotation( "annotation"), + classValue = Values.class, + + byteArrayValue = { 1, -1}, + charArrayValue = { 'c', 'b', (char)-1}, + booleanArrayValue = {true, false}, + intArrayValue = { 1, -1}, + shortArrayValue = { (short)1, (short)-1}, + longArrayValue = { 1L, -1L}, + floatArrayValue = { 1.0f, -1.0f}, + doubleArrayValue = { 1.0d, -1.0d}, + stringArrayValue = { "aa", "bb"}, + + enumArrayValue = {ValuesEnum.ONE, ValuesEnum.TWO}, + annotationArrayValue = {@ValueAttrAnnotation( "annotation1"), @ValueAttrAnnotation( "annotation2")}, + classArrayValue = {Values.class, Values.class} +) +@ValueAttrAnnotation1( "classAnnotation1") +@ValueAttrAnnotation2( "classAnnotation2") +public class Values { + + @ValueAttrAnnotation1( "fieldAnnotation1") + @ValueAttrAnnotation2( "fieldAnnotation2") + public String testfield = "test"; + + @ValueAttrAnnotation1( "methodAnnotation1") + @ValueAttrAnnotation2( "methodAnnotation2") + @ValueAttrAnnotation() + public void testMethod( + @ValueAttrAnnotation1( "param1Annotation1") + @ValueAttrAnnotation2( "param1Annotation2") String param1, + @ValueAttrAnnotation1( "param2Annotation1") + @ValueAttrAnnotation2( "param2Annotation2") int param2) { + // @ValueAttrAnnotation( "codeAnnotation") + } + +} +
diff --git a/asmx/test/conform/annotations/ValuesAnnotation.class b/asmx/test/conform/annotations/ValuesAnnotation.class new file mode 100644 index 0000000..eb35bcb --- /dev/null +++ b/asmx/test/conform/annotations/ValuesAnnotation.class Binary files differ
diff --git a/asmx/test/conform/annotations/ValuesAnnotation.java b/asmx/test/conform/annotations/ValuesAnnotation.java new file mode 100644 index 0000000..066399d --- /dev/null +++ b/asmx/test/conform/annotations/ValuesAnnotation.java
@@ -0,0 +1,42 @@ + +package annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + + +/** + * Annotation declaration with different values + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface ValuesAnnotation { + byte byteValue() default (byte) 255; + char charValue() default (char) 128; + boolean booleanValue() default true; + int intValue() default 10; + short shortValue() default (short) 20; + long longValue() default 100L; + float floatValue() default 10.0F; + double doubleValue() default 20.0D; + String stringValue() default "defaultString"; + + Class classValue() default Values.class; + ValuesEnum enumValue() default ValuesEnum.ONE; + ValueAttrAnnotation annotationValue() default @ValueAttrAnnotation; + + byte[] byteArrayValue() default {(byte) 128, (byte) 129}; + char[] charArrayValue() default { '1', '2'}; + boolean[] booleanArrayValue() default { true, false}; + int[] intArrayValue() default { 500, 501}; + short[] shortArrayValue() default { (short) 20000, (short) 2001}; + long[] longArrayValue() default { 101L, 102L}; + float[] floatArrayValue() default { 11.0F, 12.0F}; + double[] doubleArrayValue() default { 21.0D, 22.0D}; + String[] stringArrayValue() default { "11", "22"}; + + ValuesEnum[] enumArrayValue() default { ValuesEnum.ONE, ValuesEnum.TWO}; + ValueAttrAnnotation[] annotationArrayValue() default { @ValueAttrAnnotation(), @ValueAttrAnnotation("1")}; + Class[] classArrayValue() default { Values.class, Values.class}; + +} +
diff --git a/asmx/test/conform/annotations/ValuesDump.class b/asmx/test/conform/annotations/ValuesDump.class new file mode 100644 index 0000000..7245142 --- /dev/null +++ b/asmx/test/conform/annotations/ValuesDump.class Binary files differ
diff --git a/asmx/test/conform/annotations/ValuesDump.java b/asmx/test/conform/annotations/ValuesDump.java new file mode 100644 index 0000000..146a45c --- /dev/null +++ b/asmx/test/conform/annotations/ValuesDump.java
@@ -0,0 +1,151 @@ +package annotations; +import org.objectweb.asm.*; +public class ValuesDump implements Opcodes { + +public static byte[] dump () throws Exception { + +ClassWriter cw = new ClassWriter(false); +FieldVisitor fv; +MethodVisitor mv; +AnnotationVisitor av0; + +cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/Values", null, "java/lang/Object", null); + +cw.visitSource("Values.java", null); + +{ +av0 = cw.visitAnnotation("Lannotations/ValuesAnnotation;", true); +av0.visit("byteValue", new Byte((byte)1)); +av0.visit("charValue", new Character((char)65)); +av0.visit("booleanValue", new Boolean(true)); +av0.visit("intValue", new Integer(1)); +av0.visit("shortValue", new Short((short)1)); +av0.visit("longValue", new Long(1L)); +av0.visit("floatValue", new Float("1.0")); +av0.visit("doubleValue", new Double("1.0")); +av0.visit("stringValue", "A"); +av0.visitEnum("enumValue", "Lannotations/ValuesEnum;", "ONE"); +{ +AnnotationVisitor av1 = av0.visitAnnotation("annotationValue", "Lannotations/ValueAttrAnnotation;"); +av1.visit("value", "annotation"); +av1.visitEnd(); +} +av0.visit("classValue", Type.getType("Lannotations/Values;")); +av0.visit("byteArrayValue", new byte[] {1,-1}); +av0.visit("charArrayValue", new char[] {(char)99,(char)98,(char)65535}); +av0.visit("booleanArrayValue", new boolean[] {true,false}); +av0.visit("intArrayValue", new int[] {1,-1}); +av0.visit("shortArrayValue", new short[] {(short)1,(short)-1}); +av0.visit("longArrayValue", new long[] {1L,-1L}); +av0.visit("floatArrayValue", new float[] {1.0f,-1.0f}); +av0.visit("doubleArrayValue", new double[] {1.0d,-1.0d}); +{ +AnnotationVisitor av1 = av0.visitArray("stringArrayValue"); +av1.visit(null, "aa"); +av1.visit(null, "bb"); +av1.visitEnd(); +} +{ +AnnotationVisitor av1 = av0.visitArray("enumArrayValue"); +av1.visitEnum(null, "Lannotations/ValuesEnum;", "ONE"); +av1.visitEnum(null, "Lannotations/ValuesEnum;", "TWO"); +av1.visitEnd(); +} +{ +AnnotationVisitor av1 = av0.visitArray("annotationArrayValue"); +{ +AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;"); +av2.visit("value", "annotation1"); +av2.visitEnd(); +} +{ +AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;"); +av2.visit("value", "annotation2"); +av2.visitEnd(); +} +av1.visitEnd(); +} +{ +AnnotationVisitor av1 = av0.visitArray("classArrayValue"); +av1.visit(null, Type.getType("Lannotations/Values;")); +av1.visit(null, Type.getType("Lannotations/Values;")); +av1.visitEnd(); +} +av0.visitEnd(); +} +{ +av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true); +av0.visit("value", "classAnnotation1"); +av0.visitEnd(); +} +{ +av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true); +av0.visit("value", "classAnnotation2"); +av0.visitEnd(); +} +{ +fv = cw.visitField(ACC_PUBLIC, "testfield", "Ljava/lang/String;", null, null); +{ +av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true); +av0.visit("value", "fieldAnnotation1"); +av0.visitEnd(); +} +{ +av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true); +av0.visit("value", "fieldAnnotation2"); +av0.visitEnd(); +} +fv.visitEnd(); +} +{ +mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); +mv.visitVarInsn(ALOAD, 0); +mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); +mv.visitVarInsn(ALOAD, 0); +mv.visitLdcInsn("test"); +mv.visitFieldInsn(PUTFIELD, "annotations/Values", "testfield", "Ljava/lang/String;"); +mv.visitInsn(RETURN); +mv.visitMaxs(2, 1); +mv.visitEnd(); +} +{ +mv = cw.visitMethod(ACC_PUBLIC, "testMethod", "(Ljava/lang/String;I)V", null, null); +{ +av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true); +av0.visit("value", "methodAnnotation1"); +av0.visitEnd(); +} +{ +av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true); +av0.visit("value", "methodAnnotation2"); +av0.visitEnd(); +} +{ +av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation1;", true); +av0.visit("value", "param1Annotation1"); +av0.visitEnd(); +} +{ +av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation2;", true); +av0.visit("value", "param1Annotation2"); +av0.visitEnd(); +} +{ +av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation1;", true); +av0.visit("value", "param2Annotation1"); +av0.visitEnd(); +} +{ +av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation2;", true); +av0.visit("value", "param2Annotation2"); +av0.visitEnd(); +} +mv.visitInsn(RETURN); +mv.visitMaxs(0, 3); +mv.visitEnd(); +} +cw.visitEnd(); + +return cw.toByteArray(); +} +}
diff --git a/asmx/test/conform/annotations/ValuesEnum.class b/asmx/test/conform/annotations/ValuesEnum.class new file mode 100644 index 0000000..358fbd9 --- /dev/null +++ b/asmx/test/conform/annotations/ValuesEnum.class Binary files differ
diff --git a/asmx/test/conform/annotations/ValuesEnum.java b/asmx/test/conform/annotations/ValuesEnum.java new file mode 100644 index 0000000..3d59552 --- /dev/null +++ b/asmx/test/conform/annotations/ValuesEnum.java
@@ -0,0 +1,7 @@ + +package annotations; + + +public enum ValuesEnum { + ONE, TWO, THREE; +}
diff --git a/asmx/test/conform/asmifier.xml b/asmx/test/conform/asmifier.xml new file mode 100644 index 0000000..18157e8 --- /dev/null +++ b/asmx/test/conform/asmifier.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/ASMifierTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/basicverifier.xml b/asmx/test/conform/basicverifier.xml new file mode 100644 index 0000000..d122238 --- /dev/null +++ b/asmx/test/conform/basicverifier.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/BasicVerifierTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/cases/TestClassEmpty.expected b/asmx/test/conform/cases/TestClassEmpty.expected new file mode 100644 index 0000000..726ee54 --- /dev/null +++ b/asmx/test/conform/cases/TestClassEmpty.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestClassNonEmpty.expected b/asmx/test/conform/cases/TestClassNonEmpty.expected new file mode 100644 index 0000000..20f3fda --- /dev/null +++ b/asmx/test/conform/cases/TestClassNonEmpty.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestFieldGeneric.expected b/asmx/test/conform/cases/TestFieldGeneric.expected new file mode 100644 index 0000000..6aa2078 --- /dev/null +++ b/asmx/test/conform/cases/TestFieldGeneric.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestFieldSimple.expected b/asmx/test/conform/cases/TestFieldSimple.expected new file mode 100644 index 0000000..4a84ae2 --- /dev/null +++ b/asmx/test/conform/cases/TestFieldSimple.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestLocalVariable.expected b/asmx/test/conform/cases/TestLocalVariable.expected new file mode 100644 index 0000000..68b5c35 --- /dev/null +++ b/asmx/test/conform/cases/TestLocalVariable.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestLocalVariableGenericArray.expected b/asmx/test/conform/cases/TestLocalVariableGenericArray.expected new file mode 100644 index 0000000..7a4ad30 --- /dev/null +++ b/asmx/test/conform/cases/TestLocalVariableGenericArray.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestMethodReceiver.expected b/asmx/test/conform/cases/TestMethodReceiver.expected new file mode 100644 index 0000000..a5ea7b3 --- /dev/null +++ b/asmx/test/conform/cases/TestMethodReceiver.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestMethodReturnTypeGenericArray.expected b/asmx/test/conform/cases/TestMethodReturnTypeGenericArray.expected new file mode 100644 index 0000000..02a0b94 --- /dev/null +++ b/asmx/test/conform/cases/TestMethodReturnTypeGenericArray.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestObjectCreation.expected b/asmx/test/conform/cases/TestObjectCreation.expected new file mode 100644 index 0000000..ac884b8 --- /dev/null +++ b/asmx/test/conform/cases/TestObjectCreation.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestObjectCreationGenericArray.expected b/asmx/test/conform/cases/TestObjectCreationGenericArray.expected new file mode 100644 index 0000000..0346bc6 --- /dev/null +++ b/asmx/test/conform/cases/TestObjectCreationGenericArray.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestTypeTest.expected b/asmx/test/conform/cases/TestTypeTest.expected new file mode 100644 index 0000000..bd38146 --- /dev/null +++ b/asmx/test/conform/cases/TestTypeTest.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestTypecast.expected b/asmx/test/conform/cases/TestTypecast.expected new file mode 100644 index 0000000..ae93319 --- /dev/null +++ b/asmx/test/conform/cases/TestTypecast.expected Binary files differ
diff --git a/asmx/test/conform/cases/TestTypecastGenericArray.expected b/asmx/test/conform/cases/TestTypecastGenericArray.expected new file mode 100644 index 0000000..c83404e --- /dev/null +++ b/asmx/test/conform/cases/TestTypecastGenericArray.expected Binary files differ
diff --git a/asmx/test/conform/cases/largemethod.class b/asmx/test/conform/cases/largemethod.class new file mode 100644 index 0000000..c3921e7 --- /dev/null +++ b/asmx/test/conform/cases/largemethod.class Binary files differ
diff --git a/asmx/test/conform/cases/unknownattributes.class b/asmx/test/conform/cases/unknownattributes.class new file mode 100644 index 0000000..30934d1 --- /dev/null +++ b/asmx/test/conform/cases/unknownattributes.class Binary files differ
diff --git a/asmx/test/conform/checkclassadapter.xml b/asmx/test/conform/checkclassadapter.xml new file mode 100644 index 0000000..42b40b2 --- /dev/null +++ b/asmx/test/conform/checkclassadapter.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/CheckClassAdapterTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/classadapter.xml b/asmx/test/conform/classadapter.xml new file mode 100644 index 0000000..d9bb0da --- /dev/null +++ b/asmx/test/conform/classadapter.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/ClassAdapterTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/classnode.xml b/asmx/test/conform/classnode.xml new file mode 100644 index 0000000..29dc74e --- /dev/null +++ b/asmx/test/conform/classnode.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/ClassNodeTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/classreader.xml b/asmx/test/conform/classreader.xml new file mode 100644 index 0000000..570da9d --- /dev/null +++ b/asmx/test/conform/classreader.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/ClassReaderTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/classwriter.xml b/asmx/test/conform/classwriter.xml new file mode 100644 index 0000000..6c35686 --- /dev/null +++ b/asmx/test/conform/classwriter.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/ClassWriterTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/classwriter2.xml b/asmx/test/conform/classwriter2.xml new file mode 100644 index 0000000..1d1cf4b --- /dev/null +++ b/asmx/test/conform/classwriter2.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/ClassWriterTest2.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/dataflow.xml b/asmx/test/conform/dataflow.xml new file mode 100644 index 0000000..4aed226 --- /dev/null +++ b/asmx/test/conform/dataflow.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/DataflowTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/gasmifier.xml b/asmx/test/conform/gasmifier.xml new file mode 100644 index 0000000..aa679c9 --- /dev/null +++ b/asmx/test/conform/gasmifier.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/GASMifierTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/localvariablessorter.xml b/asmx/test/conform/localvariablessorter.xml new file mode 100644 index 0000000..0c41f68 --- /dev/null +++ b/asmx/test/conform/localvariablessorter.xml
@@ -0,0 +1,23 @@ +<project name="conform" default="test"> + + <target name="test"> + <junit fork="yes" + printsummary="yes" + errorproperty="test.failed" + failureproperty="test.failed"> + <batchtest fork="yes" todir="${out.test}/reports"> + <fileset dir="${test}/conform"> + <include name="**/LocalVariablesSorterTest.java"/> + </fileset> + </batchtest> + <formatter type="xml"/> + <classpath refid="test.classpath"/> + <jvmarg value="-Dasm.test=${asm.test}"/> + <jvmarg value="-Dasm.test.class=${asm.test.class}"/> + <assertions> + <enable/> + </assertions> + </junit> + </target> + +</project>
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest$ClassFilter.class b/asmx/test/conform/org/objectweb/asm/AbstractTest$ClassFilter.class new file mode 100644 index 0000000..22d133d --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AbstractTest$ClassFilter.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest$FieldFilter.class b/asmx/test/conform/org/objectweb/asm/AbstractTest$FieldFilter.class new file mode 100644 index 0000000..9022d53 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AbstractTest$FieldFilter.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest$MethodFilter.class b/asmx/test/conform/org/objectweb/asm/AbstractTest$MethodFilter.class new file mode 100644 index 0000000..613c0ad --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AbstractTest$MethodFilter.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest.class b/asmx/test/conform/org/objectweb/asm/AbstractTest.class new file mode 100644 index 0000000..233e19a --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AbstractTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest.java b/asmx/test/conform/org/objectweb/asm/AbstractTest.java new file mode 100644 index 0000000..f78acf6 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AbstractTest.java
@@ -0,0 +1,223 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.objectweb.asm.util.TraceClassVisitor; + +import junit.framework.TestCase; +import junit.framework.TestSuite; + +/** + * Super class for test suites based on a jar file. + * + * @author Eugene Kuleshov + * @author Eric Bruneton + */ +public abstract class AbstractTest extends TestCase { + + protected String n; + + protected InputStream is; + + public AbstractTest() { + super("test"); + } + + protected void init(final String n, final InputStream is) { + this.n = n; + this.is = is; + } + + protected TestSuite getSuite() throws Exception { + TestSuite suite = new TestSuite(getClass().getName()); + String files = System.getProperty("asm.test"); + String clazz = System.getProperty("asm.test.class"); + if(files==null) { + files = System.getProperty("java.home") + File.separator + "lib" + File.separator + "rt.jar"; + if(clazz==null) { + clazz = "java.lang."; + } + } + files += ","; + + while (files.indexOf(',') != -1) { + String file = files.substring(0, files.indexOf(',')); + files = files.substring(files.indexOf(',') + 1); + File f = new File(file); + if (f.isDirectory()) { + File[] fs = f.listFiles(); + for (int i = 0; i < fs.length; ++i) { + String n = fs[i].getName(); + if (n.endsWith(".class")) { + n = n.substring(0, n.length() - 6).replace('/', '.'); + if (clazz == null || n.indexOf(clazz) != -1) { + InputStream is = new FileInputStream(fs[i]); + AbstractTest t = (AbstractTest) getClass().newInstance(); + t.init(n, is); + suite.addTest(t); + } + } + } + } else { + ZipFile zip = new ZipFile(file); + Enumeration entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry e = (ZipEntry) entries.nextElement(); + String n = e.getName(); + if (n.endsWith(".class")) { + n = n.substring(0, n.length() - 6).replace('/', '.'); + if (clazz == null || n.indexOf(clazz) != -1) { + InputStream is = zip.getInputStream(e); + AbstractTest t = (AbstractTest) getClass().newInstance(); + t.init(n, is); + suite.addTest(t); + } + } + } + } + } + return suite; + } + + public abstract void test() throws Exception; + + public void assertEquals(final ClassReader cr1, final ClassReader cr2) + throws Exception + { + if (!Arrays.equals(cr1.b, cr2.b)) { + StringWriter sw1 = new StringWriter(); + StringWriter sw2 = new StringWriter(); + ClassVisitor cv1 = new TraceClassVisitor(new PrintWriter(sw1)); + ClassVisitor cv2 = new TraceClassVisitor(new PrintWriter(sw2)); + cr1.accept(new ClassFilter(cv1), false); + cr2.accept(new ClassFilter(cv2), false); + String s1 = sw1.toString(); + String s2 = sw2.toString(); + assertEquals("different data", s1, s2); + } + } + + public String getName() { + return super.getName() + ": " + n; + } + + // ------------------------------------------------------------------------- + + static class ClassFilter extends ClassAdapter { + + public ClassFilter(final ClassVisitor cv) { + super(cv); + } + + public void visitAttribute(final Attribute attr) { + // remove unknown attributes + } + + public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value) + { + return new FieldFilter(cv.visitField(access, + name, + desc, + signature, + value)); + } + + public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions) + { + return new MethodFilter(cv.visitMethod(access, + name, + desc, + signature, + exceptions)); + } + } + + static class MethodFilter extends MethodAdapter { + + public MethodFilter(final MethodVisitor mv) { + super(mv); + } + + public void visitAttribute(final Attribute attr) { + // remove unknown attributes + } + } + + static class FieldFilter implements FieldVisitor { + + FieldVisitor fv; + + public FieldFilter(final FieldVisitor fv) { + this.fv = fv; + } + + public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible) + { + return fv.visitAnnotation(desc, visible); + } + + public TypeAnnotationVisitor visitTypeAnnotation( + final String desc, + final boolean visible) + { + return fv.visitTypeAnnotation(desc, visible); + } + + public void visitAttribute(final Attribute attr) { + // remove unknown attributes + } + + public void visitEnd() { + fv.visitEnd(); + } + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/AnnotationTest$TestClassLoader.class new file mode 100644 index 0000000..0b46563 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationTest$TestClassLoader.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationTest.class b/asmx/test/conform/org/objectweb/asm/AnnotationTest.class new file mode 100644 index 0000000..c46bdfe --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationTest.java b/asmx/test/conform/org/objectweb/asm/AnnotationTest.java new file mode 100644 index 0000000..49d9a38 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationTest.java
@@ -0,0 +1,233 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import java.io.InputStream; +import java.lang.annotation.Annotation; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; + +import annotations.ValueAttrAnnotation; +import annotations.Values; +import annotations.ValuesAnnotation; +import annotations.ValuesDump; +import annotations.ValuesEnum; + +import junit.framework.TestCase; + +public class AnnotationTest extends TestCase { + + private ValuesAnnotation a; + + public AnnotationTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + TestClassLoader cl = new TestClassLoader("annotations.Values", + getClass().getClassLoader()); + Class c = cl.loadClass("annotations.Values"); + Annotation[] annotations = c.getAnnotations(); + for (int i = 0; i < annotations.length; ++i) { + if (annotations[i] instanceof ValuesAnnotation) { + a = (ValuesAnnotation) annotations[i]; + } + } + if (a == null) { + fail(); + } + } + + public void testByteValue() { + assertEquals(1, a.byteValue()); + } + + public void testCharValue() { + assertEquals('A', a.charValue()); + } + + public void testBooleanValue() { + assertEquals(true, a.booleanValue()); + } + + public void testIntValue() { + assertEquals(1, a.intValue()); + } + + public void testShortValue() { + assertEquals(1, a.shortValue()); + } + + public void testLongValue() { + assertEquals(1L, a.longValue()); + } + + public void testFloatValue() { + assertEquals(1.0f, a.floatValue(), 0.1f); + } + + public void testDoubleValue() { + assertEquals(1.0d, a.doubleValue(), 0.1d); + } + + public void testStringValue() { + assertEquals("A", a.stringValue()); + } + + public void testAnnotationValue() { + ValueAttrAnnotation ann = a.annotationValue(); + assertEquals("annotation", ann.value()); + } + + public void testEnumValue() { + ValuesEnum en = a.enumValue(); + assertEquals(ValuesEnum.ONE, en); + } + + public void testClassValue() { + Class c = a.classValue(); + assertEquals(Values.class.getName(), c.getName()); + } + + public void testByteArrayValue() { + byte[] bs = a.byteArrayValue(); + assertEquals(1, bs[0]); + assertEquals(-1, bs[1]); + } + + public void testCharArrayValue() { + char[] bs = a.charArrayValue(); + assertEquals('c', bs[0]); + assertEquals('b', bs[1]); + assertEquals((char) -1, bs[2]); + } + + public void testBooleanArrayValue() { + boolean[] bs = a.booleanArrayValue(); + assertEquals(true, bs[0]); + assertEquals(false, bs[1]); + } + + public void testIntArrayValue() { + int[] bs = a.intArrayValue(); + assertEquals(1, bs[0]); + assertEquals(-1, bs[1]); + } + + public void testShortArrayValue() { + short[] bs = a.shortArrayValue(); + assertEquals(1, bs[0]); + assertEquals(-1, bs[1]); + } + + public void testLongArrayValue() { + long[] bs = a.longArrayValue(); + assertEquals(1L, bs[0]); + assertEquals(-1L, bs[1]); + } + + public void testFloatArrayValue() { + float[] bs = a.floatArrayValue(); + assertEquals(1.0f, bs[0], 0.1f); + assertEquals(-1.0f, bs[1], 0.1f); + } + + public void testDoubleArrayValue() { + double[] bs = a.doubleArrayValue(); + assertEquals(1.0d, bs[0], 0.1d); + assertEquals(-1.0d, bs[1], 0.1d); + } + + public void testStringArrayValue() { + String[] s = a.stringArrayValue(); + assertEquals("aa", s[0]); + assertEquals("bb", s[1]); + } + + public void testAnnotationArrayValue() { + ValueAttrAnnotation[] ann = a.annotationArrayValue(); + assertEquals("annotation1", ann[0].value()); + assertEquals("annotation2", ann[1].value()); + } + + public void testEnumArrayValue() { + ValuesEnum[] en = a.enumArrayValue(); + assertEquals(ValuesEnum.ONE, en[0]); + assertEquals(ValuesEnum.TWO, en[1]); + } + + public void testClassArrayValue() { + Class[] c = a.classArrayValue(); + assertEquals(Values.class.getName(), c[0].getName()); + assertEquals(Values.class.getName(), c[1].getName()); + } + + // issue 303711 + public void testMethodNode() throws Exception { + InputStream is = getClass().getResourceAsStream("/" + + annotations.Values.class.getName().replace('.', '/') + + ".class"); + ClassReader cr = new ClassReader(is); + ClassNode cn = new ClassNode(); + cr.accept(cn, false); + } + + private static final class TestClassLoader extends ClassLoader { + + private final String className; + + private final ClassLoader loader; + + public TestClassLoader(String className, ClassLoader loader) { + super(); + this.className = className; + this.loader = loader; + } + + public Class loadClass(String name) throws ClassNotFoundException { + if (className.equals(name)) { + try { + byte[] bytecode = ValuesDump.dump(); + return super.defineClass(className, + bytecode, + 0, + bytecode.length); + } catch (Exception ex) { + ex.printStackTrace(); + throw new ClassNotFoundException("Load error: " + + ex.toString(), ex); + } + } + + return loader.loadClass(name); + } + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationMismatchException.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationMismatchException.class new file mode 100644 index 0000000..36f0df4 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationMismatchException.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationRecorder.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationRecorder.class new file mode 100644 index 0000000..19ca304 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationRecorder.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ClassRecorder.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ClassRecorder.class new file mode 100644 index 0000000..9ec0502 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ClassRecorder.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ParameterDescription.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ParameterDescription.class new file mode 100644 index 0000000..d4de753 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ParameterDescription.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.class new file mode 100644 index 0000000..0a3ab8b --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.java b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.java new file mode 100644 index 0000000..4703099 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.java
@@ -0,0 +1,517 @@ +package org.objectweb.asm; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.TypeAnnotationVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.commons.EmptyVisitor; + +/** + * An <code>AnnotationVerifier</code> provides a way to check to see if two + * versions of the same class (from two different <code>.class</code> files), + * have the same annotations on the same elements. + */ +public class AnnotationVerifier { + + /** + * The "correct" version of the class to verify against. + */ + private ClassRecorder originalVisitor; + + /** + * The uncertain version of the class to verify. + */ + private ClassRecorder newVisitor; + + /** + * Constructs a new <code>AnnotationVerifier</code> that does not yet have + * any information about the class. + */ + public AnnotationVerifier() { + originalVisitor = new ClassRecorder(); + newVisitor = new ClassRecorder(); + } + + /** + * Returns the <code>ClassVisitor</code> which should be made to visit the + * version of the class known to be correct. + * + * @return a visitor for the good version of the class + */ + public ClassVisitor originalVisitor() { + return originalVisitor; + } + + /** + * Returns the <code>ClassVisitor</code> which should be made to visit the + * version of the class being tested. + * + * @return a visitor the the experimental version of the class + */ + public ClassVisitor newVisitor() { + return newVisitor; + } + + /** + * Verifies that the visitors returned by {@link #originalVisitor()} and + * {@link #newVisitor()} have visited the same class. This method can only + * be called if both visitors have already visited a class. + * + * @throws AnnotationMismatchException if the two visitors have not visited + * two versions of the same class that contain idential annotations. + */ + public void verify() { + if(!newVisitor.name.equals(originalVisitor.name)) { + throw new AnnotationMismatchException( + "Cannot verify two different classes " + + newVisitor.name + " cannot be verified against " + + originalVisitor.name); + } + newVisitor.verifyAgainst(originalVisitor); + } + + /** + * A ClassRecorder records all the annotations that it visits, and serves + * as a ClassVisitor, FieldVisitor and MethodVisitor. + */ + private class ClassRecorder extends EmptyVisitor { + + private String description; + + public String name; + private String signature; + + private Map<String, ClassRecorder> fieldRecorders; + // key is unparameterized name + + private Map<String, ClassRecorder> methodRecorders; + // key is complete method signature + + // general annotations + private Map<String, AnnotationRecorder> anns; + private Map<String, AnnotationRecorder> xanns; + + //method specific annotations + private Set<AnnotationRecorder> danns; + private Map<ParameterDescription, AnnotationRecorder> panns; + + public ClassRecorder() { + this("[class: ","",""); + } + + public ClassRecorder(String internalDescription, String name, String signature) { + this.description = internalDescription; + this.name = name; + this.signature = signature; + + fieldRecorders = new HashMap<String, ClassRecorder>(); + methodRecorders = new HashMap<String, ClassRecorder>(); + + anns = new HashMap<String, AnnotationRecorder>(); + xanns = new HashMap<String, AnnotationRecorder>(); + + danns = new HashSet<AnnotationRecorder>(); + panns = new HashMap<ParameterDescription, AnnotationRecorder>(); + } + + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + this.name = name; + this.signature = signature; + description = description + name; + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + AnnotationRecorder av = new AnnotationRecorder( + description + " annotation: " + desc); + anns.put(desc,av); + return av; + } + + public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible) { + AnnotationRecorder av = new AnnotationRecorder( + description + " annotation: " + desc); + xanns.put(desc,av); + return av; + } + + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + ClassRecorder fr = + new ClassRecorder( + description + " field: " + name, + name, signature); + fieldRecorders.put(name, fr); + return fr; + } + + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + ClassRecorder mr = + new ClassRecorder( + description + " method: " + name + desc, name+desc, signature); + methodRecorders.put(name+desc, mr); + return mr; + } + + // MethodVisitor methods: + public AnnotationVisitor visitAnnotationDefault() { + AnnotationRecorder dr = new AnnotationRecorder( + description + " default annotation"); + danns.add(dr); + return dr; + } + + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { + ParameterDescription pd = + new ParameterDescription(parameter, desc, visible); + AnnotationRecorder pr = + new AnnotationRecorder(description + + " parameter annotation: " + pd); + panns.put(pd, pr); + return pr; + } + + public void verifyAgainst(ClassRecorder correct) { + // first, ensure all annotations are correct + verifyAnns(this.anns, correct.anns); + verifyAnns(this.xanns, correct.xanns); + + // then recurse into any annotations on fields/methods + verifyMemberAnns(this.fieldRecorders, correct.fieldRecorders); + verifyMemberAnns(this.methodRecorders, correct.methodRecorders); + } + + private void verifyAnns( + Map<String, AnnotationRecorder> questionableAnns, + Map<String, AnnotationRecorder> correctAnns) { + Set<AnnotationRecorder> unresolvedQuestionableAnns = + new HashSet<AnnotationRecorder>(questionableAnns.values()); + + for(Map.Entry<String, AnnotationRecorder> entry : + correctAnns.entrySet()) { + String name = entry.getKey(); + AnnotationRecorder correctAnn = entry.getValue(); + AnnotationRecorder questionableAnn = questionableAnns.get(name); + if(questionableAnn == null) { + throw new AnnotationMismatchException(description + + " does not contain expected annotation: " + correctAnn); + } + + questionableAnn.verifyAgainst(correctAnn); + + unresolvedQuestionableAnns.remove(questionableAnn); + } + + for(AnnotationRecorder unexpectedAnnOnThis : unresolvedQuestionableAnns) { + throw new AnnotationMismatchException(description + + " contains unexpected annotation : " + unexpectedAnnOnThis); + } + } + + private void verifyMemberAnns( + Map<String, ClassRecorder> questionableMembers, + Map<String, ClassRecorder> correctMembers) { + Set<ClassRecorder> unresolvedQuestionableMembers = + new HashSet<ClassRecorder>(questionableMembers.values()); + + for(Map.Entry<String, ClassRecorder> entry : + correctMembers.entrySet()) { + String name = entry.getKey(); + ClassRecorder correctMember = entry.getValue(); + ClassRecorder questionableMember = questionableMembers.get(name); + if(questionableMember == null) { + throw new AnnotationMismatchException(description + + " does not contain expected member: " + correctMember); + } + + questionableMember.verifyAgainst(correctMember); + + unresolvedQuestionableMembers.remove(questionableMember); + } + + for(ClassRecorder unexpectedMemberOnThis : unresolvedQuestionableMembers) { + System.out.println("Going to throw exception: "); + System.out.println("questionable: " + questionableMembers); + System.out.println("correct: " + correctMembers); + + throw new AnnotationMismatchException(description + + " contains unexpected member: " + unexpectedMemberOnThis); + } + } + + public String toString() { + return description; + } + } + + /** + * An AnnotationRecorder is an TypeAnnotationVisitor that records all the + * information it visits. + */ + private class AnnotationRecorder implements TypeAnnotationVisitor { + private String description; + + private List<String> fieldArgs1; + private List<Object> fieldArgs2; + + private List<String> enumArgs1; + private List<String> enumArgs2; + private List<String> enumArgs3; + + private List<String> innerAnnotationArgs1; + private List<String> innerAnnotationArgs2; + private Map<String, AnnotationRecorder> innerAnnotationMap; + + private List<String> arrayArgs; + private Map<String, AnnotationRecorder> arrayMap; + + private List<Integer> xIndexArgs; + private List<Integer> xLengthArgs; + private List<Integer> xLocationArgs; + private List<Integer> xLocationLengthArgs; + private List<Integer> xOffsetArgs; + private List<Integer> xStartPcArgs; + private List<Integer> xTargetTypeArgs; + private List<Integer> xParamIndexArgs; + private List<Integer> xBoundIndexArgs; + private List<Integer> xTypeIndexArgs; + + public AnnotationRecorder(String description) { + this.description = description; + fieldArgs1 = new ArrayList<String>(); + fieldArgs2 = new ArrayList<Object>(); + + enumArgs1 = new ArrayList<String>(); + enumArgs2 = new ArrayList<String>(); + enumArgs3 = new ArrayList<String>(); + + innerAnnotationArgs1 = new ArrayList<String>(); + innerAnnotationArgs2 = new ArrayList<String>(); + innerAnnotationMap = new HashMap<String, AnnotationRecorder>(); + + arrayArgs = new ArrayList<String>(); + arrayMap = new HashMap<String, AnnotationRecorder>(); + + xIndexArgs = new ArrayList<Integer>(); + xLengthArgs = new ArrayList<Integer>(); + xLocationArgs = new ArrayList<Integer>(); + xLocationLengthArgs = new ArrayList<Integer>(); + xOffsetArgs = new ArrayList<Integer>(); + xStartPcArgs = new ArrayList<Integer>(); + xTargetTypeArgs = new ArrayList<Integer>(); + xParamIndexArgs = new ArrayList<Integer>(); + xBoundIndexArgs = new ArrayList<Integer>(); + xTypeIndexArgs = new ArrayList<Integer>(); + } + + public void visitXIndex(int index) { + xIndexArgs.add(index); + } + + public void visitXLength(int length) { + xLengthArgs.add(length); + } + + public void visitXLocation(int location) { + xLocationArgs.add(location); + } + + public void visitXLocationLength(int location_length) { + xLocationLengthArgs.add(location_length); + } + + public void visitXOffset(int offset) { + xOffsetArgs.add(offset); + } + + @Override + public void visitXNumEntries(int num_entries) { } + + public void visitXStartPc(int start_pc) { + xStartPcArgs.add(start_pc); + } + + public void visitXTargetType(int target_type) { + xTargetTypeArgs.add(target_type); + } + + public void visitXParamIndex(int param_index) { + xParamIndexArgs.add(param_index); + } + + public void visitXBoundIndex(int bound_index) { + xBoundIndexArgs.add(bound_index); + } + + public void visitXTypeIndex(int type_index) { + xTypeIndexArgs.add(type_index); + } + + public void visit(String name, Object value) { + fieldArgs1.add(name); + fieldArgs2.add(value); + } + + public AnnotationVisitor visitAnnotation(String name, String desc) { + innerAnnotationArgs1.add(name); + innerAnnotationArgs2.add(name); + + AnnotationRecorder av= new AnnotationRecorder(description + name); + innerAnnotationMap.put(name, av); + return av; + } + + public AnnotationVisitor visitArray(String name) { + arrayArgs.add(name); + AnnotationRecorder av = new AnnotationRecorder(description + name); + arrayMap.put(name, av); + return av; + } + + public void visitEnd() { + } + + public void visitEnum(String name, String desc, String value) { + enumArgs1.add(name); + enumArgs2.add(desc); + enumArgs3.add(value); + } + + public String toString() { + return description; + } + + /** + * Checks that the information passed into this matches the information + * passed into another AnnotationRecorder. For right now, the order in + * which information is passed in does matter. If there is a conflict in + * information, an exception will be thrown. + * + * @param ar an annotation recorder that has visited the correct information + * this should visit + * @throws AnnotationMismatchException if the information visited by this + * does not match the information in ar + */ + public void verifyAgainst(AnnotationRecorder ar) { + StringBuilder sb = new StringBuilder(); + verifyList(sb, "visit()", 1, this.fieldArgs1, ar.fieldArgs1); + verifyList(sb, "visit()", 2, this.fieldArgs2, ar.fieldArgs2); + + verifyList(sb, "visitEnum()", 1, this.enumArgs1, ar.enumArgs1); + verifyList(sb, "visitEnum()", 2, this.enumArgs2, ar.enumArgs2); + verifyList(sb, "visitEnum()", 3, this.enumArgs3, ar.enumArgs3); + + verifyList(sb, "visitAnnotation()", 1, this.innerAnnotationArgs1, ar.innerAnnotationArgs1); + verifyList(sb, "visitAnnotation()", 2, this.innerAnnotationArgs2, ar.innerAnnotationArgs2); + + verifyList(sb, "visitArray()", 1, this.arrayArgs, ar.arrayArgs); + + verifyList(sb, "visitXIndexArgs()", 1, this.xIndexArgs, ar.xIndexArgs); + verifyList(sb, "visitXLength()", 1, this.xLengthArgs, ar.xIndexArgs); + verifyList(sb, "visitXLocation()", 1, this.xLocationArgs, ar.xLocationArgs); + verifyList(sb, "visitXLocationLength()", 1, this.xLocationLengthArgs, ar.xLocationLengthArgs); + verifyList(sb, "visitXOffset()", 1, this.xOffsetArgs, ar.xOffsetArgs); + verifyList(sb, "visitXStartPc()", 1, this.xStartPcArgs, ar.xStartPcArgs); + verifyList(sb, "visitXTargetType()", 1, this.xTargetTypeArgs, ar.xTargetTypeArgs); + verifyList(sb, "visitXParamIndex()", 1, this.xParamIndexArgs, ar.xParamIndexArgs); + verifyList(sb, "visitXBoundIndex()", 1, this.xBoundIndexArgs, ar.xBoundIndexArgs); + verifyList(sb, "visitXTypeIndex()", 1, this.xTypeIndexArgs, ar.xTypeIndexArgs); + + verifyInnerAnnotationRecorder(sb, this.innerAnnotationMap, ar.innerAnnotationMap); + verifyInnerAnnotationRecorder(sb, this.arrayMap, ar.arrayMap); + + if(sb.length() > 0) { + throw new AnnotationMismatchException(sb.toString()); + } + } + + private void verifyList( + StringBuilder sb, + String methodName, + int parameter, + List questionable, + List correct) { + if(!questionable.equals(correct)) { + String s = "\n" + description + + " was called with unexpected information in parameter: " + parameter + + "\nReceived: " + questionable + + "\nExpected: " + correct; + } + } + + private void verifyInnerAnnotationRecorder( + StringBuilder sb, + Map<String, AnnotationRecorder> questionableAR, + Map<String, AnnotationRecorder> correctAR) { + // checks on arguments passed in to the methods that created these + // AnnotationRecorders (i.e. the checks on the String keys on these maps) + // ensure that these have identical keys + for(Map.Entry<String, AnnotationRecorder> questionableEntry : + questionableAR.entrySet()) { + questionableEntry.getValue().verifyAgainst( + correctAR.get(questionableEntry.getClass())); + } + } + + } + + /** + * A ParameterDescription is a convenient class used to keep information about + * method parameters. Parameters are equal if they have the same index, + * regardless of their description. + */ + private class ParameterDescription { + public final int parameter; + public final String desc; + public final boolean visible; + + public ParameterDescription(int parameter, String desc, boolean visible) { + this.parameter = parameter; + this.desc = desc; + this.visible = visible; + } + + public boolean equals(Object o) { + if(o instanceof ParameterDescription) { + ParameterDescription p = (ParameterDescription) o; + return this.parameter == p.parameter; + } + return false; + } + + public int hashCode() { + return parameter * 17; + } + + public String toString() { + return + "parameter index: " + parameter + + " desc: " + desc + + " visible: " + visible; + } + } + + /** + * An AnnotationMismatchException is an Exception that indicates that + * two versions of the same class do not have the same annotations on + * either the class, its field, or its methods. + */ + public class AnnotationMismatchException extends RuntimeException { + private static final long serialVersionUID = 20060714L; // today's date + + /** + * Constructs a new AnnotationMismatchException with the given error message. + * + * @param msg the error as to why the annotations do not match. + */ + public AnnotationMismatchException(String msg) { + super(msg); + } + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassAdapterTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest$TestClassLoader.class new file mode 100644 index 0000000..23d2089 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest$TestClassLoader.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.class b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.class new file mode 100644 index 0000000..1b10f6a --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.java b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.java new file mode 100644 index 0000000..74c7743 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.java
@@ -0,0 +1,68 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import junit.framework.TestSuite; + +/** + * ClassAdapter tests. + * + * @author Eric Bruneton + */ +public class ClassAdapterTest extends AbstractTest { + + private final static TestClassLoader LOADER = new TestClassLoader(); + + public static TestSuite suite() throws Exception { + return new ClassAdapterTest().getSuite(); + } + + public void test() throws Exception { + ClassReader cr = new ClassReader(is); + ClassWriter cw = new ClassWriter(true, true); + cr.accept(new ClassAdapter(cw), false); + byte[] b = cw.toByteArray(); + try { + LOADER.defineClass(n, b); + } catch (ClassFormatError cfe) { + fail(cfe.getMessage()); + } catch (Throwable ignored) { + } + } + + // ------------------------------------------------------------------------ + + static class TestClassLoader extends ClassLoader { + + public Class defineClass(final String name, final byte[] b) { + return defineClass(name, b, 0, b.length); + } + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassReaderTest.class b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.class new file mode 100644 index 0000000..ad5bb5a --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassReaderTest.java b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.java new file mode 100644 index 0000000..aad1800 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.java
@@ -0,0 +1,50 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import junit.framework.TestSuite; + +import org.objectweb.asm.commons.EmptyVisitor; + +/** + * ClassReader tests. + * + * @author Eric Bruneton + */ +public class ClassReaderTest extends AbstractTest { + + public static TestSuite suite() throws Exception { + return new ClassReaderTest().getSuite(); + } + + public void test() throws Exception { + new ClassReader(is).accept(new EmptyVisitor(), false); + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest.class b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.class new file mode 100644 index 0000000..3c21eef --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest.java b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.java new file mode 100644 index 0000000..be0a27a --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.java
@@ -0,0 +1,51 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import junit.framework.TestSuite; + +/** + * ClassWriter tests. + * + * @author Eric Bruneton + */ +public class ClassWriterTest extends AbstractTest { + + public static TestSuite suite() throws Exception { + return new ClassWriterTest().getSuite(); + } + + public void test() throws Exception { + ClassReader cr = new ClassReader(is); + ClassWriter cw = new ClassWriter(false, true); + cr.accept(cw, false); + assertEquals(cr, new ClassReader(cw.toByteArray())); + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.class b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.class new file mode 100644 index 0000000..6154eb0 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.java b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.java new file mode 100644 index 0000000..d83a2f3 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.java
@@ -0,0 +1,51 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import junit.framework.TestSuite; + +/** + * ClassWriter tests for copyPool() optimization. + * + * @author Eugene Kuleshov + */ +public class ClassWriterTest2 extends AbstractTest { + + public static TestSuite suite() throws Exception { + return new ClassWriterTest2().getSuite(); + } + + public void test() throws Exception { + ClassReader cr = new ClassReader(is); + ClassWriter cw = new ClassWriter(cr, false); + cr.accept(cw, false); + assertEquals(cr, new ClassReader(cw.toByteArray())); + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest$TestClassLoader.class new file mode 100644 index 0000000..3202177 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest$TestClassLoader.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.class b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.class new file mode 100644 index 0000000..73d5be1 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.java b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.java new file mode 100644 index 0000000..df90694 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.java
@@ -0,0 +1,399 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; + +import junit.framework.TestCase; + +import org.objectweb.asm.tree.ClassNode; + +import annotations.ExtendedValuesDump; +import annotations.ValueAttrAnnotation; +import annotations.Values; +import annotations.ValuesAnnotation; +import annotations.ValuesEnum; + +public class TypeAnnotationTest extends TestCase { + + private ValuesAnnotation a; + + public TypeAnnotationTest(String name) { + super(name); + } + + protected void setUp() throws Exception { + TestClassLoader cl = new TestClassLoader("annotations.Values", + getClass().getClassLoader()); + Class c = cl.loadClass("annotations.Values"); + Annotation[] annotations = c.getAnnotations(); + for (int i = 0; i < annotations.length; ++i) { + if (annotations[i] instanceof ValuesAnnotation) { + a = (ValuesAnnotation) annotations[i]; + } + } + if (a == null) { + fail(); + } + } + + public void testByteValue() { + assertEquals(1, a.byteValue()); + } + + public void testCharValue() { + assertEquals('A', a.charValue()); + } + + public void testBooleanValue() { + assertEquals(true, a.booleanValue()); + } + + public void testIntValue() { + assertEquals(1, a.intValue()); + } + + public void testShortValue() { + assertEquals(1, a.shortValue()); + } + + public void testLongValue() { + assertEquals(1L, a.longValue()); + } + + public void testFloatValue() { + assertEquals(1.0f, a.floatValue(), 0.1f); + } + + public void testDoubleValue() { + assertEquals(1.0d, a.doubleValue(), 0.1d); + } + + public void testStringValue() { + assertEquals("A", a.stringValue()); + } + + public void testAnnotationValue() { + ValueAttrAnnotation ann = a.annotationValue(); + assertEquals("annotation", ann.value()); + } + + public void testEnumValue() { + ValuesEnum en = a.enumValue(); + assertEquals(ValuesEnum.ONE, en); + } + + public void testClassValue() { + Class c = a.classValue(); + assertEquals(Values.class.getName(), c.getName()); + } + + public void testByteArrayValue() { + byte[] bs = a.byteArrayValue(); + assertEquals(1, bs[0]); + assertEquals(-1, bs[1]); + } + + public void testCharArrayValue() { + char[] bs = a.charArrayValue(); + assertEquals('c', bs[0]); + assertEquals('b', bs[1]); + assertEquals((char) -1, bs[2]); + } + + public void testBooleanArrayValue() { + boolean[] bs = a.booleanArrayValue(); + assertEquals(true, bs[0]); + assertEquals(false, bs[1]); + } + + public void testIntArrayValue() { + int[] bs = a.intArrayValue(); + assertEquals(1, bs[0]); + assertEquals(-1, bs[1]); + } + + public void testShortArrayValue() { + short[] bs = a.shortArrayValue(); + assertEquals(1, bs[0]); + assertEquals(-1, bs[1]); + } + + public void testLongArrayValue() { + long[] bs = a.longArrayValue(); + assertEquals(1L, bs[0]); + assertEquals(-1L, bs[1]); + } + + public void testFloatArrayValue() { + float[] bs = a.floatArrayValue(); + assertEquals(1.0f, bs[0], 0.1f); + assertEquals(-1.0f, bs[1], 0.1f); + } + + public void testDoubleArrayValue() { + double[] bs = a.doubleArrayValue(); + assertEquals(1.0d, bs[0], 0.1d); + assertEquals(-1.0d, bs[1], 0.1d); + } + + public void testStringArrayValue() { + String[] s = a.stringArrayValue(); + assertEquals("aa", s[0]); + assertEquals("bb", s[1]); + } + + public void testAnnotationArrayValue() { + ValueAttrAnnotation[] ann = a.annotationArrayValue(); + assertEquals("annotation1", ann[0].value()); + assertEquals("annotation2", ann[1].value()); + } + + public void testEnumArrayValue() { + ValuesEnum[] en = a.enumArrayValue(); + assertEquals(ValuesEnum.ONE, en[0]); + assertEquals(ValuesEnum.TWO, en[1]); + } + + public void testClassArrayValue() { + Class[] c = a.classArrayValue(); + assertEquals(Values.class.getName(), c[0].getName()); + assertEquals(Values.class.getName(), c[1].getName()); + } + + // issue 303711 + public void testMethodNode() throws Exception { + InputStream is = getClass().getResourceAsStream("/" + + annotations.Values.class.getName().replace('.', '/') + + ".class"); + ClassReader cr = new ClassReader(is); + ClassNode cn = new ClassNode(); + cr.accept(cn, false); + } + + // TODO: figure out a more general way of finding a file given the + // different ways of executing something in Eclipse, ant, and sub ant + public String nameExpected(String className) { + Class cls = this.getClass(); + ProtectionDomain pr = cls.getProtectionDomain(); + CodeSource cs = pr.getCodeSource(); + URL loc = cs.getLocation(); + String s = loc.toString(); + if(s.startsWith("file:")) { + s = s.substring(5); + } + // in the default asmx ant script, class will be run from something like + // asmx/output/... so just cut things off at asmx + s = s.substring(0, s.lastIndexOf("asmx")+4); + return s+"/test/conform/cases/"+className+".expected"; + } + + /** + * Runs a test on the given class by loading up its expected file and + * ensuring that they match the given bytecodes. + * + * @param className the name of the class to test + * @param bytecodes the bytecodes that are to be tested to be sure they + * match the arrays in the given class. + */ + public void testRunner(String className, byte[] bytecodes) throws Exception { + InputStream isExpected = new FileInputStream(nameExpected(className)); + + ClassReader crCorrect = new ClassReader(isExpected); + ClassReader crGenerated = new ClassReader(bytecodes); + + AnnotationVerifier av = new AnnotationVerifier(); + + crCorrect.accept(av.originalVisitor(), false); + crGenerated.accept(av.newVisitor(), false); + try { + av.verify(); + } catch(Exception e) { + fail("AnnotationMismatch: " + e.toString()); + } + } + + /** + * Runs an extended annotation test on TestClassEmpty. + * + * @throws Exception if unexpected error occurs + */ + public void testClassEmpty() throws Exception { + testRunner("TestClassEmpty", ExtendedValuesDump.dumpClassEmpty()); + } + + /** + * Runs an extended annotation test on TestClassNonEmpty. + * + * @throws Exception if unexpected error occurs + */ + public void testClassNonEmpty() throws Exception { + testRunner("TestClassNonEmpty", ExtendedValuesDump.dumpClassNonEmpty()); + } + + /** + * Runs an extended annotation test on TestFieldSimple. + * + * @throws Exception if unexpected error occurs + */ + public void testFieldSimple() throws Exception { + testRunner("TestFieldSimple", ExtendedValuesDump.dumpFieldSimple()); + } + + /** + * Runs an extended annotation test on TestFieldGeneric. + * + * @throws Exception if unexpected error occurs + */ + public void testFieldGeneric() throws Exception { + testRunner("TestFieldGeneric", ExtendedValuesDump.dumpFieldGeneric()); + } + + /** + * Runs an extended annotation test on TestLocalVariable. + * + * @throws Exception if unexpected error occurs + */ + public void testLocalVariable() throws Exception { + testRunner("TestLocalVariable", ExtendedValuesDump.dumpLocalVariable()); + } + + /** + * Runs an extended annotation test on TestLocalVariableGenericArray. + * + * @throws Exception if unexpected error occurs + */ + public void testLocalVariableGenericArray() throws Exception { + testRunner("TestLocalVariableGenericArray", + ExtendedValuesDump.dumpLocalVariableGenericArray()); + } + + /** + * Runs an extended annotation test on TestMethodReceiver. + * + * @throws Exception if unexpected error occurs + */ + public void testMethodReceiver() throws Exception { + testRunner("TestMethodReceiver", ExtendedValuesDump.dumpMethodReceiver()); + } + + /** + * Runs an extended annotation test on TestMethodReturnTypeGenericArray. + * + * @throws Exception if unexpected error occurs + */ + public void testMethodReturnTypeGenericArray() throws Exception { + testRunner("TestMethodReturnTypeGenericArray", + ExtendedValuesDump.dumpMethodReturnTypeGenericArray()); + } + + /** + * Runs an extended annotation test on TestObjectCreation. + * + * @throws Exception if unexpected error occurs + */ + public void testObjectCreation() throws Exception { + testRunner("TestObjectCreation", ExtendedValuesDump.dumpObjectCreation()); + } + + /** + * Runs an extended annotation test on TestObjectCreationGenericArray. + * + * @throws Exception if unexpected error occurs + */ + public void testObjectCreationGenericArray() throws Exception { + testRunner("TestObjectCreationGenericArray", + ExtendedValuesDump.dumpObjectCreationGenericArray()); + } + + /** + * Runs an extended annotation test on TestTypecast. + * + * @throws Exception if unexpected error occurs + */ + public void testTypecast() throws Exception { + testRunner("TestTypecast", ExtendedValuesDump.dumpTypecast()); + } + + /** + * Runs an extended annotation test on TestTypecastGenericArray. + * + * @throws Exception if unexpected error occurs + */ + public void testTypecastGenericArray() throws Exception { + testRunner("TestTypecastGenericArray", + ExtendedValuesDump.dumpTypecastGenericArray()); + } + + /** + * Runs an extended annotation test on TestTypeTest. + * + * @throws Exception if unexpected error occurs + */ + public void testTypeTest() throws Exception { + testRunner("TestTypeTest", ExtendedValuesDump.dumpTypeTest()); + } + + private static final class TestClassLoader extends ClassLoader { + + private final String className; + + private final ClassLoader loader; + + public TestClassLoader(String className, ClassLoader loader) { + super(); + this.className = className; + this.loader = loader; + } + + public Class loadClass(String name) throws ClassNotFoundException { + if (className.equals(name)) { + try { + byte[] bytecode = ExtendedValuesDump.dump(); + return super.defineClass(className, + bytecode, + 0, + bytecode.length); + } catch (Exception ex) { + ex.printStackTrace(); + throw new ClassNotFoundException("Load error: " + + ex.toString(), ex); + } + } + + return loader.loadClass(name); + } + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/UnitTest.class b/asmx/test/conform/org/objectweb/asm/UnitTest.class new file mode 100644 index 0000000..b801d1d --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/UnitTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/UnitTest.java b/asmx/test/conform/org/objectweb/asm/UnitTest.java new file mode 100644 index 0000000..fa621da --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/UnitTest.java
@@ -0,0 +1,71 @@ +package org.objectweb.asm; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Arrays; + +import junit.framework.TestCase; + +public class UnitTest extends TestCase { + + public void testReader() throws IOException { + try { + new ClassReader((InputStream) null); + fail(); + } catch (IOException e) { + } + + ClassReader cr = new ClassReader(getClass().getName()); + int item = cr.getItem(1); + assertTrue(item >= 10); + assertTrue(item < cr.header); + } + + public void testWriter() { + ClassWriter cw = new ClassWriter(false); + cw.newConst(new Byte((byte) 0)); + cw.newConst(new Character('0')); + cw.newConst(new Short((short) 0)); + cw.newConst(new Boolean(false)); + try { + cw.newConst(new Object()); + fail(); + } catch (RuntimeException e) { + } + cw.newMethod("A", "m", "()V", false); + } + + public void testLabel() { + new Label().toString(); + try { + new Label().getOffset(); + fail(); + } catch (RuntimeException e) { + } + } + + public void testType() { + assertEquals(Type.getType(Integer.TYPE), Type.INT_TYPE); + assertEquals(Type.getType(Void.TYPE), Type.VOID_TYPE); + assertEquals(Type.getType(Boolean.TYPE), Type.BOOLEAN_TYPE); + assertEquals(Type.getType(Byte.TYPE), Type.BYTE_TYPE); + assertEquals(Type.getType(Character.TYPE), Type.CHAR_TYPE); + assertEquals(Type.getType(Short.TYPE), Type.SHORT_TYPE); + assertEquals(Type.getType(Double.TYPE), Type.DOUBLE_TYPE); + assertEquals(Type.getType(Float.TYPE), Type.FLOAT_TYPE); + assertEquals(Type.getType(Long.TYPE), Type.LONG_TYPE); + String s1 = Type.getType(UnitTest.class).getInternalName(); + String s2 = Type.getInternalName(UnitTest.class); + assertEquals(s1, s2); + for (int i = 0; i < Arrays.class.getMethods().length; ++i) { + Method m = Arrays.class.getMethods()[i]; + Type[] args = Type.getArgumentTypes(m); + Type r = Type.getReturnType(m); + String d1 = Type.getMethodDescriptor(r, args); + String d2 = Type.getMethodDescriptor(m); + assertEquals(d1, d2); + } + Type.INT_TYPE.hashCode(); + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.class b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.class new file mode 100644 index 0000000..28172cc --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.java b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.java new file mode 100644 index 0000000..6bc03c8 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.java
@@ -0,0 +1,79 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.objectweb.asm.attrs; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Arrays; + +import org.objectweb.asm.AbstractTest; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.util.TraceClassVisitor; +import org.objectweb.asm.util.attrs.ASMStackMapTableAttribute; + +/** + * StackMapTableAttributeTest + * + * @author Eugene Kuleshov + */ +public class StackMapTableAttributeTest extends AbstractTest { + + public static final String TEST_CLASS = "StackMapTableSample.data"; + + public StackMapTableAttributeTest() { + super(); + is = getClass().getResourceAsStream(TEST_CLASS); + } + + public void test() throws Exception { + Attribute[] attributes = new Attribute[] { new ASMStackMapTableAttribute() }; + + ClassWriter cw = new ClassWriter(false); + + ClassReader cr1 = new ClassReader(is); + cr1.accept(cw, attributes, true); + + ClassReader cr2 = new ClassReader(cw.toByteArray()); + + if (!Arrays.equals(cr1.b, cr2.b)) { + StringWriter sw1 = new StringWriter(); + StringWriter sw2 = new StringWriter(); + ClassVisitor cv1 = new TraceClassVisitor(new PrintWriter(sw1)); + ClassVisitor cv2 = new TraceClassVisitor(new PrintWriter(sw2)); + cr1.accept(cv1, attributes, true); + cr2.accept(cv2, attributes, true); + assertEquals("different data", sw1.toString(), sw2.toString()); + } + + } +}
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.class b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.class new file mode 100644 index 0000000..14a4481 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.class Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.data b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.data new file mode 100644 index 0000000..d3b9067 --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.data Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.java b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.java new file mode 100644 index 0000000..68534fd --- /dev/null +++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.java
@@ -0,0 +1,141 @@ +/*** + * ASM tests + * Copyright (c) 2002-2005 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 o