-Quick start instructions
-========================
+Installation Instructions
+*************************
-You can check the configure options via `./configure --help'. But the
-default settings should be ok. For building the package type:
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
- $ ./configure
- $ make
- $ make install
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
-The default installation prefix is `/usr/local/cacao'. You can change
-this destination by providing the `--prefix=PATH' option to configure.
+Basic Installation
+==================
-This version of cacao only supports the `--prefix' option, even if
-configure processes the other options (`--bindir', `--libdir', etc.),
-it will screw up your installation since CACAO tries to setup a
-directory tree like the JVMs from Sun or IBM do.
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
-Requirements:
--------------
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 6. Often, you can also type `make uninstall' to remove the installed
+ files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
-In order to compile the Java classes from GNU classpath, you need a
-Java compiler supported by GNU classpath. To get a working classpath
-in an appropriate time, we recommend IBM jikes 1.18 or newer. For
-further instructions concerning GNU classpath, please refer to
-`src/classpath/INSTALL'.
JAVA_ARCH="m68k"
;;
-mips | mipsel )
+mips )
ARCH_DIR="mips"
ARCH_FLAGS="-D__MIPS__"
- dnl Is this correct for mipsel?
JAVA_ARCH="mips"
;;
+mipsel )
+ ARCH_DIR="mips"
+ ARCH_FLAGS="-D__MIPS__"
+ JAVA_ARCH="mipsel"
+ ;;
+
powerpc )
ARCH_DIR="powerpc"
ARCH_FLAGS="-m32 -D__POWERPC__"
AC_CHECK_FUNCS([strncmp])
AC_CHECK_FUNCS([strstr])
AC_CHECK_FUNCS([time])
-AC_CHECK_FUNCS([va_end])
-AC_CHECK_FUNCS([va_start])
AC_CHECK_FUNCS([write])
--- /dev/null
+dnl m4/jitcache.m4
+dnl
+dnl Copyright (C) 2008
+dnl CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+dnl
+dnl This file is part of CACAO.
+dnl
+dnl This program is free software; you can redistribute it and/or
+dnl modify it under the terms of the GNU General Public License as
+dnl published by the Free Software Foundation; either version 2, or (at
+dnl your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+dnl 02110-1301, USA.
+
+
+dnl check for jitcache support
+AC_DEFUN([AC_CHECK_ENABLE_JITCACHE],[
+AC_MSG_CHECKING(whether JIT compiler output caching should be enabled)
+AC_ARG_ENABLE([jitcache],
+ [AS_HELP_STRING(--enable-jitcache,enable caching of JIT compiler output [[default=no]])],
+ [case "${enableval}" in
+ yes) ENABLE_JITCACHE=yes;;
+ *) ENABLE_JITCACHE=no;;
+ esac],
+ [ENABLE_JITCACHE=no])
+AC_MSG_RESULT(${ENABLE_JITCACHE})
+AM_CONDITIONAL([ENABLE_JITCACHE], test x"${ENABLE_JITCACHE}" = "xyes")
+
+if test x"${ENABLE_JITCACHE}" = "xyes"; then
+ AC_DEFINE([ENABLE_JITCACHE], 1, [store and load JIT compiler output])
+fi
+])
# define DYNAMIC_LOADING
extern int _end[];
# define DATAEND (_end)
- extern int __data_start[];
-# define DATASTART ((ptr_t)(__data_start))
+# define SEARCH_FOR_DATA_START
# define CPP_WORDSZ _MIPS_SZPTR
# define ALIGNMENT (_MIPS_SZPTR/8)
# if __GLIBC__ == 2 && __GLIBC_MINOR__ >= 2 || __GLIBC__ > 2
# define OS_TYPE "LINUX"
# define LINUX_STACKBOTTOM
# define DYNAMIC_LOADING
- extern int __data_start[];
-# define DATASTART ((ptr_t)(__data_start))
- extern int _end[];
+# define SEARCH_FOR_DATA_START
+ extern int _end[];
# define DATAEND (_end)
# define CACHE_LINE_SIZE 256
# define GETPAGESIZE() 4096
#include "final.h"
#include "heap.h"
#include "mm/memory.h"
-#include "vm/finalizer.h"
+#include "vm/finalizer.hpp"
/* Global Variables ***********************************************************/
#include "vm/types.h"
#include "toolbox/list.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* Global Variables ***********************************************************/
#include "mm/memory.h"
#include "toolbox/logging.h"
-#include "vm/finalizer.h"
+#include "vm/finalizer.hpp"
#include "vm/rt-timing.h"
#include "vm/vm.hpp"
#include "toolbox/logging.h"
#include "vm/global.h"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/vm.hpp"
#include "threads/thread.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/replace.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/exceptions.hpp"
-#include "vm/finalizer.h"
+#include "vm/finalizer.hpp"
#include "vm/global.h"
#include "vm/loader.hpp"
#include "vm/options.h"
// Includes.
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* function prototypes ********************************************************/
#include "vm/jit/builtin.hpp"
#include "vm/jit/asmpart.h"
#include "vm/class.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "mm/gc.hpp"
#include "toolbox/logging.h"
#include "vm/options.h"
#include "vm/types.h"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* localref_table **************************************************************
#include "vm/class.hpp"
#include "vm/global.h"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/os.hpp"
#include "vm/utf8.h"
#include "vm/string.hpp"
#include "vm/types.h"
#include "vm/vm.hpp" /* REMOVE ME: temporarily */
-#include "vm/zip.h"
+#include "vm/zip.hpp"
static java_handle_t* zip_read_resource(list_classpath_entry *lce, utf *name)
#include "toolbox/logging.h"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/utf8.h"
#include "vm/vm.hpp"
#include "toolbox/logging.h"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/utf8.h"
#include "vm/vm.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/class.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/exceptions.hpp"
#include "vm/globals.hpp"
#include "vm/initialize.hpp"
#include "vm/javaobjects.hpp"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/loader.hpp"
#include "vm/options.h"
#include "vm/primitive.hpp"
#include "vm/statistics.h"
#include "vm/string.hpp"
#include "vm/vm.hpp"
-#include "vm/zip.h"
+#include "vm/zip.hpp"
#include "vm/jit/asmpart.h"
# include "native/vm/reflection.hpp"
#endif
-#include "vm/access.h"
+#include "vm/access.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/exceptions.hpp"
#include "vm/global.h"
#include "native/vm/reflection.hpp"
-#include "vm/access.h"
+#include "vm/access.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/class.hpp"
#include "vm/exceptions.hpp"
#include "vm/globals.hpp"
#include "vm/initialize.hpp"
#include "vm/javaobjects.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/resolve.hpp"
#include "vm/string.hpp"
#include "vm/class.hpp"
#include "vm/initialize.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/os.hpp"
#endif
#include "vm/jit/builtin.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/exceptions.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
jint JVM_Send(jint fd, char *buf, jint nBytes, jint flags)
{
- log_println("JVM_Send: IMPLEMENT ME!");
+ TRACEJVMCALLSENTER(("JVM_Send(fd=%d, buf=%p, nBytes=%d, flags=%d", fd, buf, nBytes, flags));
- return 0;
+ int result = os::send(fd, buf, nBytes, flags);
+
+ TRACEJVMCALLSEXIT(("->%d", result));
+
+ return result;
}
#include "native/vm/reflection.hpp"
-#include "vm/access.h"
+#include "vm/access.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/exceptions.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
#include "vm/initialize.hpp"
#include "vm/javaobjects.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/string.hpp"
#include "native/native.hpp"
#include "vm/field.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
class Reflection {
}
+/*
+ * Class: sun/misc/Unsafe
+ * Method: getDouble
+ * Signature: (J)D
+ */
+JNIEXPORT jdouble JNICALL Java_sun_misc_Unsafe_getDouble__J(JNIEnv *env, jobject _this, jlong address)
+{
+ double *p;
+ double value;
+
+ p = (double*) (intptr_t) address;
+
+ value = *p;
+
+ return value;
+}
+
+
+/*
+ * Class: sun/misc/Unsafe
+ * Method: putDouble
+ * Signature: (JD)V
+ */
+JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putDouble__JD(JNIEnv *env, jobject _this, jlong address, jdouble value)
+{
+ double* p;
+
+ p = (double*) (intptr_t) address;
+
+ *p = value;
+}
+
+
/*
* Class: sun/misc/Unsafe
* Method: objectFieldOffset
{ (char*) "putLong", (char*) "(JJ)V", (void*) (uintptr_t) &Java_sun_misc_Unsafe_putLong__JJ },
{ (char*) "getFloat", (char*) "(J)F", (void*) (uintptr_t) &Java_sun_misc_Unsafe_getFloat__J },
{ (char*) "putFloat", (char*) "(JF)V", (void*) (uintptr_t) &Java_sun_misc_Unsafe_putFloat__JF },
+ { (char*) "getDouble", (char*) "(J)D", (void*) (uintptr_t) &Java_sun_misc_Unsafe_getDouble__J },
+ { (char*) "putDouble", (char*) "(JD)V", (void*) (uintptr_t) &Java_sun_misc_Unsafe_putDouble__JD },
{ (char*) "objectFieldOffset", (char*) "(Ljava/lang/reflect/Field;)J", (void*) (uintptr_t) &Java_sun_misc_Unsafe_objectFieldOffset },
{ (char*) "allocateMemory", (char*) "(J)J", (void*) (uintptr_t) &Java_sun_misc_Unsafe_allocateMemory },
#if 0
#include "toolbox/list.hpp"
#include "vm/exceptions.hpp"
-#include "vm/finalizer.h"
+#include "vm/finalizer.hpp"
#include "vm/global.h"
#include "vm/options.h"
#include "vm/string.hpp"
#include "vm/exceptions.hpp"
#include "vm/globals.hpp"
#include "vm/javaobjects.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/string.hpp"
#include "vm/utf8.h"
#include <stdarg.h>
#include "vm/class.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/utf8.h"
if ENABLE_ZLIB
ZLIB_SOURCES = \
- zip.c \
- zip.h
+ zip.cpp \
+ zip.hpp
endif
noinst_HEADERS = \
libvm.la
libvm_la_SOURCES = \
- access.c \
- access.h \
+ access.cpp \
+ access.hpp \
$(ANNOTATION_SOURCES) \
array.cpp \
array.hpp \
$(ASSERTION_SOURCES) \
class.cpp \
class.hpp \
- classcache.c \
- classcache.h \
+ classcache.cpp \
+ classcache.hpp \
$(CYCLES_STATS_SOURCES) \
- descriptor.c \
- descriptor.h \
+ descriptor.cpp \
+ descriptor.hpp \
exceptions.cpp \
exceptions.hpp \
field.cpp \
field.hpp \
- finalizer.c \
- finalizer.h \
+ finalizer.cpp \
+ finalizer.hpp \
globals.cpp \
globals.hpp \
initialize.cpp \
initialize.hpp \
javaobjects.cpp \
javaobjects.hpp \
- linker.c \
- linker.h \
+ linker.cpp \
+ linker.hpp \
loader.cpp \
loader.hpp \
- method.c \
- method.h \
+ method.cpp \
+ method.hpp \
options.c \
options.h \
os.cpp \
+++ /dev/null
-/* src/vm/access.c - checking access rights
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <string.h>
-
-#include "vm/types.h"
-
-#include "mm/memory.h"
-
-#include "native/llni.h"
-
-#include "vm/access.h"
-#include "vm/jit/builtin.hpp"
-#include "vm/class.hpp"
-#include "vm/exceptions.hpp"
-#include "vm/field.hpp"
-#include "vm/globals.hpp"
-#include "vm/method.h"
-
-#include "vm/jit/stacktrace.hpp"
-
-
-/* access_is_accessible_class **************************************************
-
- Check if a class is accessible from another class
-
- IN:
- referer..........the class containing the reference
- cls..............the result of resolving the reference
-
- RETURN VALUE:
- true.............access permitted
- false............access denied
-
- NOTE:
- This function performs the checks listed in section 5.4.4.
- "Access Control" of "The Java(TM) Virtual Machine Specification,
- Second Edition".
-
-*******************************************************************************/
-
-bool access_is_accessible_class(classinfo *referer, classinfo *cls)
-{
- assert(referer);
- assert(cls);
-
- /* Public classes are always accessible. */
-
- if (cls->flags & ACC_PUBLIC)
- return true;
-
- /* A class in the same package is always accessible. */
-
- if (SAME_PACKAGE(referer, cls))
- return true;
-
-#if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
- /* Code for Sun's OpenJDK (see
- hotspot/src/share/vm/runtime/reflection.cpp
- (Reflection::verify_class_access)): Allow all accesses from
- sun/reflect/MagicAccessorImpl subclasses to succeed
- trivially. */
-
- /* NOTE: This check must be before checks that could return
- false. */
-
- if (class_issubclass(referer, class_sun_reflect_MagicAccessorImpl))
- return true;
-#endif
-
- /* A non-public class in another package is not accessible. */
-
- return false;
-}
-
-
-/* access_is_accessible_member *************************************************
-
- Check if a field or method is accessible from a given class
-
- IN:
- referer..........the class containing the reference
- declarer.........the class declaring the member
- memberflags......the access flags of the member
-
- RETURN VALUE:
- true.............access permitted
- false............access denied
-
- NOTE:
- This function only performs the checks listed in section 5.4.4.
- "Access Control" of "The Java(TM) Virtual Machine Specification,
- Second Edition".
-
- In particular a special condition for protected access with is
- part of the verification process according to the spec is not
- checked in this function.
-
-*******************************************************************************/
-
-bool access_is_accessible_member(classinfo *referer, classinfo *declarer,
- s4 memberflags)
-{
- assert(referer);
- assert(declarer);
-
- /* Public members are accessible. */
-
- if (memberflags & ACC_PUBLIC)
- return true;
-
-#if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
- /* Code for Sun's OpenJDK (see
- hotspot/src/share/vm/runtime/reflection.cpp
- (Reflection::verify_class_access)): Allow all accesses from
- sun/reflect/MagicAccessorImpl subclasses to succeed
- trivially. */
-
- /* NOTE: This check must be before checks that could return
- false. */
-
- if (class_issubclass(referer, class_sun_reflect_MagicAccessorImpl))
- return true;
-#endif
-
- /* {declarer is not an interface} */
-
- /* private members are only accessible by the class itself */
-
- if (memberflags & ACC_PRIVATE)
- return (referer == declarer);
-
- /* {the member is protected or package private} */
-
- /* protected and package private members are accessible in the
- same package */
-
- if (SAME_PACKAGE(referer, declarer))
- return true;
-
- /* package private members are not accessible outside the package */
-
- if (!(memberflags & ACC_PROTECTED))
- return false;
-
- /* {the member is protected and declarer is in another package} */
-
- /* a necessary condition for access is that referer is a subclass
- of declarer */
-
- assert((referer->state & CLASS_LINKED) && (declarer->state & CLASS_LINKED));
-
- if (class_isanysubclass(referer, declarer))
- return true;
-
- return false;
-}
-
-
-/* access_check_field **********************************************************
-
- Check if the (indirect) caller has access rights to the specified
- field.
-
- IN:
- f................the field to check
- callerdepth......number of callers to ignore
- For example if the stacktrace looks like this:
-
- [0] java.lang.reflect.Method.invokeNative (Native Method)
- [1] java.lang.reflect.Method.invoke
- [2] <caller>
-
- you must specify 2 so the access rights of <caller>
- are checked.
-
- RETURN VALUE:
- true.............access permitted
- false............access denied, an exception has been thrown
-
-*******************************************************************************/
-
-#if defined(ENABLE_JAVASE)
-bool access_check_field(fieldinfo *f, int callerdepth)
-{
- classinfo *callerclass;
- char *msg;
- int msglen;
- utf *u;
-
- /* If everything is public, there is nothing to check. */
-
- if ((f->clazz->flags & ACC_PUBLIC) && (f->flags & ACC_PUBLIC))
- return true;
-
- /* Get the caller's class. */
-
- callerclass = stacktrace_get_caller_class(callerdepth);
-
- if (callerclass == NULL)
- return false;
-
- /* Check access rights. */
-
- if (!access_is_accessible_member(callerclass, f->clazz, f->flags)) {
- msglen =
- utf_bytes(f->clazz->name) +
- strlen(".") +
- utf_bytes(f->name) +
- strlen(" not accessible from ") +
- utf_bytes(callerclass->name) +
- strlen("0");
-
- msg = MNEW(char, msglen);
-
- utf_copy_classname(msg, f->clazz->name);
- strcat(msg, ".");
- utf_cat_classname(msg, f->name);
- strcat(msg, " not accessible from ");
- utf_cat_classname(msg, callerclass->name);
-
- u = utf_new_char(msg);
-
- MFREE(msg, char, msglen);
-
- exceptions_throw_illegalaccessexception(u);
-
- return false;
- }
-
- /* access granted */
-
- return true;
-}
-#endif
-
-
-/* access_check_method *********************************************************
-
- Check if the (indirect) caller has access rights to the specified
- method.
-
- IN:
- m................the method to check
- callerdepth......number of callers to ignore
- For example if the stacktrace looks like this:
-
- [1] java.lang.reflect.Method.invokeNative (Native Method)
- [1] java.lang.reflect.Method.invoke
- [2] <caller>
-
- you must specify 2 so the access rights of <caller>
- are checked.
-
- RETURN VALUE:
- true.............access permitted
- false............access denied, an exception has been thrown
-
-*******************************************************************************/
-
-#if defined(ENABLE_JAVASE)
-bool access_check_method(methodinfo *m, int callerdepth)
-{
- classinfo *callerclass;
- char *msg;
- int msglen;
- utf *u;
-
- /* If everything is public, there is nothing to check. */
-
- if ((m->clazz->flags & ACC_PUBLIC) && (m->flags & ACC_PUBLIC))
- return true;
-
- /* Get the caller's class. */
-
- callerclass = stacktrace_get_caller_class(callerdepth);
-
- if (callerclass == NULL)
- return false;
-
- /* Check access rights. */
-
- if (!access_is_accessible_member(callerclass, m->clazz, m->flags)) {
- msglen =
- utf_bytes(m->clazz->name) +
- strlen(".") +
- utf_bytes(m->name) +
- utf_bytes(m->descriptor) +
- strlen(" not accessible from ") +
- utf_bytes(callerclass->name) +
- strlen("0");
-
- msg = MNEW(char, msglen);
-
- utf_copy_classname(msg, m->clazz->name);
- strcat(msg, ".");
- utf_cat_classname(msg, m->name);
- utf_cat_classname(msg, m->descriptor);
- strcat(msg, " not accessible from ");
- utf_cat_classname(msg, callerclass->name);
-
- u = utf_new_char(msg);
-
- MFREE(msg, char, msglen);
-
- exceptions_throw_illegalaccessexception(u);
-
- return false;
- }
-
- /* access granted */
-
- return true;
-}
-#endif
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
-
--- /dev/null
+/* src/vm/access.c - checking access rights
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "native/llni.h"
+
+#include "vm/access.hpp"
+#include "vm/jit/builtin.hpp"
+#include "vm/class.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/field.hpp"
+#include "vm/globals.hpp"
+#include "vm/method.hpp"
+
+#include "vm/jit/stacktrace.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* access_is_accessible_class **************************************************
+
+ Check if a class is accessible from another class
+
+ IN:
+ referer..........the class containing the reference
+ cls..............the result of resolving the reference
+
+ RETURN VALUE:
+ true.............access permitted
+ false............access denied
+
+ NOTE:
+ This function performs the checks listed in section 5.4.4.
+ "Access Control" of "The Java(TM) Virtual Machine Specification,
+ Second Edition".
+
+*******************************************************************************/
+
+bool access_is_accessible_class(classinfo *referer, classinfo *cls)
+{
+ assert(referer);
+ assert(cls);
+
+ /* Public classes are always accessible. */
+
+ if (cls->flags & ACC_PUBLIC)
+ return true;
+
+ /* A class in the same package is always accessible. */
+
+ if (SAME_PACKAGE(referer, cls))
+ return true;
+
+#if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
+ /* Code for Sun's OpenJDK (see
+ hotspot/src/share/vm/runtime/reflection.cpp
+ (Reflection::verify_class_access)): Allow all accesses from
+ sun/reflect/MagicAccessorImpl subclasses to succeed
+ trivially. */
+
+ /* NOTE: This check must be before checks that could return
+ false. */
+
+ if (class_issubclass(referer, class_sun_reflect_MagicAccessorImpl))
+ return true;
+#endif
+
+ /* A non-public class in another package is not accessible. */
+
+ return false;
+}
+
+
+/* access_is_accessible_member *************************************************
+
+ Check if a field or method is accessible from a given class
+
+ IN:
+ referer..........the class containing the reference
+ declarer.........the class declaring the member
+ memberflags......the access flags of the member
+
+ RETURN VALUE:
+ true.............access permitted
+ false............access denied
+
+ NOTE:
+ This function only performs the checks listed in section 5.4.4.
+ "Access Control" of "The Java(TM) Virtual Machine Specification,
+ Second Edition".
+
+ In particular a special condition for protected access with is
+ part of the verification process according to the spec is not
+ checked in this function.
+
+*******************************************************************************/
+
+bool access_is_accessible_member(classinfo *referer, classinfo *declarer,
+ s4 memberflags)
+{
+ assert(referer);
+ assert(declarer);
+
+ /* Public members are accessible. */
+
+ if (memberflags & ACC_PUBLIC)
+ return true;
+
+#if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
+ /* Code for Sun's OpenJDK (see
+ hotspot/src/share/vm/runtime/reflection.cpp
+ (Reflection::verify_class_access)): Allow all accesses from
+ sun/reflect/MagicAccessorImpl subclasses to succeed
+ trivially. */
+
+ /* NOTE: This check must be before checks that could return
+ false. */
+
+ if (class_issubclass(referer, class_sun_reflect_MagicAccessorImpl))
+ return true;
+#endif
+
+ /* {declarer is not an interface} */
+
+ /* private members are only accessible by the class itself */
+
+ if (memberflags & ACC_PRIVATE)
+ return (referer == declarer);
+
+ /* {the member is protected or package private} */
+
+ /* protected and package private members are accessible in the
+ same package */
+
+ if (SAME_PACKAGE(referer, declarer))
+ return true;
+
+ /* package private members are not accessible outside the package */
+
+ if (!(memberflags & ACC_PROTECTED))
+ return false;
+
+ /* {the member is protected and declarer is in another package} */
+
+ /* a necessary condition for access is that referer is a subclass
+ of declarer */
+
+ assert((referer->state & CLASS_LINKED) && (declarer->state & CLASS_LINKED));
+
+ if (class_isanysubclass(referer, declarer))
+ return true;
+
+ return false;
+}
+
+
+/* access_check_field **********************************************************
+
+ Check if the (indirect) caller has access rights to the specified
+ field.
+
+ IN:
+ f................the field to check
+ callerdepth......number of callers to ignore
+ For example if the stacktrace looks like this:
+
+ [0] java.lang.reflect.Method.invokeNative (Native Method)
+ [1] java.lang.reflect.Method.invoke
+ [2] <caller>
+
+ you must specify 2 so the access rights of <caller>
+ are checked.
+
+ RETURN VALUE:
+ true.............access permitted
+ false............access denied, an exception has been thrown
+
+*******************************************************************************/
+
+#if defined(ENABLE_JAVASE)
+bool access_check_field(fieldinfo *f, int callerdepth)
+{
+ classinfo *callerclass;
+ char *msg;
+ int msglen;
+ utf *u;
+
+ /* If everything is public, there is nothing to check. */
+
+ if ((f->clazz->flags & ACC_PUBLIC) && (f->flags & ACC_PUBLIC))
+ return true;
+
+ /* Get the caller's class. */
+
+ callerclass = stacktrace_get_caller_class(callerdepth);
+
+ if (callerclass == NULL)
+ return false;
+
+ /* Check access rights. */
+
+ if (!access_is_accessible_member(callerclass, f->clazz, f->flags)) {
+ msglen =
+ utf_bytes(f->clazz->name) +
+ strlen(".") +
+ utf_bytes(f->name) +
+ strlen(" not accessible from ") +
+ utf_bytes(callerclass->name) +
+ strlen("0");
+
+ msg = MNEW(char, msglen);
+
+ utf_copy_classname(msg, f->clazz->name);
+ strcat(msg, ".");
+ utf_cat_classname(msg, f->name);
+ strcat(msg, " not accessible from ");
+ utf_cat_classname(msg, callerclass->name);
+
+ u = utf_new_char(msg);
+
+ MFREE(msg, char, msglen);
+
+ exceptions_throw_illegalaccessexception(u);
+
+ return false;
+ }
+
+ /* access granted */
+
+ return true;
+}
+#endif
+
+
+/* access_check_method *********************************************************
+
+ Check if the (indirect) caller has access rights to the specified
+ method.
+
+ IN:
+ m................the method to check
+ callerdepth......number of callers to ignore
+ For example if the stacktrace looks like this:
+
+ [1] java.lang.reflect.Method.invokeNative (Native Method)
+ [1] java.lang.reflect.Method.invoke
+ [2] <caller>
+
+ you must specify 2 so the access rights of <caller>
+ are checked.
+
+ RETURN VALUE:
+ true.............access permitted
+ false............access denied, an exception has been thrown
+
+*******************************************************************************/
+
+#if defined(ENABLE_JAVASE)
+bool access_check_method(methodinfo *m, int callerdepth)
+{
+ classinfo *callerclass;
+ char *msg;
+ int msglen;
+ utf *u;
+
+ /* If everything is public, there is nothing to check. */
+
+ if ((m->clazz->flags & ACC_PUBLIC) && (m->flags & ACC_PUBLIC))
+ return true;
+
+ /* Get the caller's class. */
+
+ callerclass = stacktrace_get_caller_class(callerdepth);
+
+ if (callerclass == NULL)
+ return false;
+
+ /* Check access rights. */
+
+ if (!access_is_accessible_member(callerclass, m->clazz, m->flags)) {
+ msglen =
+ utf_bytes(m->clazz->name) +
+ strlen(".") +
+ utf_bytes(m->name) +
+ utf_bytes(m->descriptor) +
+ strlen(" not accessible from ") +
+ utf_bytes(callerclass->name) +
+ strlen("0");
+
+ msg = MNEW(char, msglen);
+
+ utf_copy_classname(msg, m->clazz->name);
+ strcat(msg, ".");
+ utf_cat_classname(msg, m->name);
+ utf_cat_classname(msg, m->descriptor);
+ strcat(msg, " not accessible from ");
+ utf_cat_classname(msg, callerclass->name);
+
+ u = utf_new_char(msg);
+
+ MFREE(msg, char, msglen);
+
+ exceptions_throw_illegalaccessexception(u);
+
+ return false;
+ }
+
+ /* access granted */
+
+ return true;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+
+++ /dev/null
-/* src/vm/access.h - checking access rights
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _ACCESS_H
-#define _ACCESS_H
-
-#include "config.h"
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "vm/class.hpp"
-#include "vm/field.hpp"
-#include "vm/global.h"
-#include "vm/method.h"
-
-
-/* macros *********************************************************************/
-
-#define SAME_PACKAGE(a,b) \
- ((a)->classloader == (b)->classloader && \
- (a)->packagename == (b)->packagename)
-
-
-/* function prototypes ********************************************************/
-
-bool access_is_accessible_class(classinfo *referer, classinfo *cls);
-
-bool access_is_accessible_member(classinfo *referer, classinfo *declarer,
- int32_t memberflags);
-
-#if defined(ENABLE_JAVASE)
-bool access_check_field(fieldinfo *f, int callerdepth);
-bool access_check_method(methodinfo *m, int callerdepth);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ACCESS_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
-
--- /dev/null
+/* src/vm/access.h - checking access rights
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _ACCESS_H
+#define _ACCESS_H
+
+#include "config.h"
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "vm/class.hpp"
+#include "vm/field.hpp"
+#include "vm/global.h"
+#include "vm/method.hpp"
+
+
+/* macros *********************************************************************/
+
+#define SAME_PACKAGE(a,b) \
+ ((a)->classloader == (b)->classloader && \
+ (a)->packagename == (b)->packagename)
+
+
+/* function prototypes ********************************************************/
+
+bool access_is_accessible_class(classinfo *referer, classinfo *cls);
+
+bool access_is_accessible_member(classinfo *referer, classinfo *declarer,
+ int32_t memberflags);
+
+#if defined(ENABLE_JAVASE)
+bool access_check_field(fieldinfo *f, int callerdepth);
+bool access_check_method(methodinfo *m, int callerdepth);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ACCESS_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+
#include "vm/field.hpp"
#include "vm/global.h"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* function prototypes ********************************************************/
#include "vm/array.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/class.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/exceptions.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
#include "vm/javaobjects.hpp"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/loader.hpp"
#include "vm/options.h"
#include "vm/resolve.hpp"
#include "vm/field.hpp"
#include "vm/global.h"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/references.h"
#include "vm/string.hpp"
#include "vm/utf8.h"
+++ /dev/null
-/* src/vm/classcache.c - loaded class cache and loading constraints
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-
-#include "vm/types.h"
-
-#include "mm/memory.h"
-
-#include "threads/lock.hpp"
-#include "threads/mutex.hpp"
-
-#include "toolbox/hashtable.h"
-#include "toolbox/logging.h"
-
-#include "vm/classcache.h"
-#include "vm/exceptions.hpp"
-#include "vm/options.h"
-#include "vm/utf8.h"
-
-
-/*************************************************************************
-
- Class Cache
-
- The classcache has two functions:
-
- 1) caching the resolution of class references
- 2) storing and checking loading constraints
-
- We will use the following terms in this description:
-
- N a class name: a utf string
- (N,L) a class reference with initiating loader L and class name N
- C a class (object): the result of resolving a reference (N,L)
- We will write resultion as
- C = *(N,L)
- (N,L1,L2) a loading constraint indicating that (N,L1) and (N,L2) must
- resolve to the same class C. So (N,L1,L2) means
- *(N,L1) = *(N,L2)
-
- The functions of the classcache require:
-
- 1) a mapping (N,L) |--> C for looking up prior resolution results.
- 2) storing the current set of loading constraints { (N,L1,L2) }
-
- These functions can be rearranged like that:
-
- a mapping N |--> (a mapping L |--> C or NULL,
- a set of constraints {(L1,L2)})
-
- Thus we can treat the mapping and constraints for each name N
- separately. The implementation does this by keeping a hash table
- mapping a name N to a `classcache_name_entry` which contains all
- info with respect to N.
-
- For a class name N we can define an equivalence relation ~N~ on
- class loaders:
-
- L1 ~N~ L2 <==> *(N,L1) = *(N,L2)
-
- A loading constraint (N,L1,L2) implies L1 ~N~ L2.
-
- Also, if two references (N,L1) and (N,L2) resolve to the same class C
- we have L1 ~N~ L2 because class loaders are required to return
- consistent resolutions for a name N [XXX].
-
- A `classcache_name_entry` keeps a set of tuples { (Cx,IL,CL) },
- where
- Cx...is a class C or NULL
- IL...is the set of initiating loaders
- CL...is the set of constrained loaders
-
- Such a tuple is called `classcache_class_entry` in the source code.
-
- The following holds for each tuple (Cx,IL,CL):
-
- . (Cx is NULL) implies IL = {}.
-
- . If Cx is a class, IL is the set of loaders that have been
- recorded as initiating loaders for Cx. IL may be the
- empty set {} in case Cx has already been defined but no
- initiating loader has been recorded, yet.
-
- . (IL u CL) is a subset of an equivalence class of ~N~.
-
- (This means that all loaders in IL and CL must resolve
- the name N to the same class.)
-
- The following holds for the set of tuples { (Cx,IL,CL) }:
-
- . For a given class C there is at most one tuple with Cx = C
- in the set. (There may be an arbitrary number of tuples
- with Cx = NULL, however.)
-
- . For a given loader L there is at most one tuple with
- L in (IL u CL).
-
- The implementation stores sets of loaders as linked lists of
- `classcache_loader_entry`s.
-
- Comments about manipulating the classcache can be found in the
- individual functions below.
-
-*************************************************************************/
-
-
-/* initial number of slots in the classcache hash table */
-#define CLASSCACHE_INIT_SIZE 2048
-
-/*============================================================================*/
-/* DEBUG HELPERS */
-/*============================================================================*/
-
-/* #define CLASSCACHE_VERBOSE */
-
-/*============================================================================*/
-/* STATISTICS */
-/*============================================================================*/
-
-/*#define CLASSCACHE_STATS*/
-
-#ifdef CLASSCACHE_STATS
-static int stat_classnames_stored = 0;
-static int stat_classes_stored = 0;
-static int stat_trivial_constraints = 0;
-static int stat_nontriv_constraints = 0;
-static int stat_nontriv_constraints_both = 0;
-static int stat_nontriv_constraints_merged = 0;
-static int stat_nontriv_constraints_one = 0;
-static int stat_nontriv_constraints_none = 0;
-static int stat_new_loader_entry = 0;
-static int stat_merge_class_entries = 0;
-static int stat_merge_loader_entries = 0;
-static int stat_lookup = 0;
-static int stat_lookup_class_entry_checked = 0;
-static int stat_lookup_loader_checked = 0;
-static int stat_lookup_name = 0;
-static int stat_lookup_name_entry = 0;
-static int stat_lookup_name_notfound = 0;
-static int stat_lookup_new_name = 0;
-static int stat_lookup_new_name_entry = 0;
-static int stat_lookup_new_name_collisions = 0;
-static int stat_rehash_names = 0;
-static int stat_rehash_names_collisions = 0;
-
-#define CLASSCACHE_COUNT(cnt) (cnt)++
-#define CLASSCACHE_COUNTIF(cond,cnt) do{if(cond) (cnt)++;} while(0)
-
-void classcache_print_statistics(FILE *file) {
- fprintf(file,"classnames stored : %8d\n",stat_classnames_stored);
- fprintf(file,"classes stored : %8d\n",stat_classes_stored);
- fprintf(file,"trivial constraints : %8d\n",stat_trivial_constraints);
- fprintf(file,"non-triv constraints: %8d\n",stat_nontriv_constraints);
- fprintf(file," both loaders rec.: %8d\n",stat_nontriv_constraints_both);
- fprintf(file," merged : %8d\n",stat_nontriv_constraints_merged);
- fprintf(file," one loader rec. : %8d\n",stat_nontriv_constraints_one);
- fprintf(file," no loaders rec. : %8d\n",stat_nontriv_constraints_none);
- fprintf(file,"new loader entries : %8d\n",stat_new_loader_entry);
- fprintf(file,"merge class entries : %8d\n",stat_merge_class_entries);
- fprintf(file,"merge loader entries: %8d\n",stat_merge_loader_entries);
- fprintf(file,"lookups : %8d\n",stat_lookup);
- fprintf(file," class entries ckd: %8d\n",stat_lookup_class_entry_checked);
- fprintf(file," loader checked : %8d\n",stat_lookup_loader_checked);
- fprintf(file,"lookup name : %8d\n",stat_lookup_name);
- fprintf(file," entries checked : %8d\n",stat_lookup_name_entry);
- fprintf(file," not found : %8d\n",stat_lookup_name_notfound);
- fprintf(file,"lookup (new) name : %8d\n",stat_lookup_new_name);
- fprintf(file," entries checked : %8d\n",stat_lookup_new_name_entry);
- fprintf(file," new collisions : %8d\n",stat_lookup_new_name_collisions);
- fprintf(file,"names rehashed : %8d times\n",stat_rehash_names);
- fprintf(file," collisions : %8d\n",stat_rehash_names_collisions);
-}
-#else
-#define CLASSCACHE_COUNT(cnt)
-#define CLASSCACHE_COUNTIF(cond,cnt)
-#endif
-
-/*============================================================================*/
-/* THREAD-SAFE LOCKING */
-/*============================================================================*/
-
- /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
- /* CAUTION: The static functions below are */
- /* NOT synchronized! */
- /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
-
-#if defined(ENABLE_THREADS)
-# define CLASSCACHE_LOCK() Mutex_lock(classcache_hashtable_mutex)
-# define CLASSCACHE_UNLOCK() Mutex_unlock(classcache_hashtable_mutex)
-#else
-# define CLASSCACHE_LOCK()
-# define CLASSCACHE_UNLOCK()
-#endif
-
-/*============================================================================*/
-/* GLOBAL VARIABLES */
-/*============================================================================*/
-
-hashtable hashtable_classcache;
-
-#if defined(ENABLE_THREADS)
-static Mutex *classcache_hashtable_mutex;
-#endif
-
-
-/*============================================================================*/
-/* */
-/*============================================================================*/
-
-/* prototypes */
-
-static void classcache_free_class_entry(classcache_class_entry *clsen);
-static void classcache_remove_class_entry(classcache_name_entry *en,
- classcache_class_entry *clsen);
-
-/* hash function to use */
-
-#define CLASSCACHE_HASH utf_full_hashkey
-
-/* classcache_init *************************************************************
-
- Initialize the class cache
-
- Note: NOT synchronized!
-
-*******************************************************************************/
-
-bool classcache_init(void)
-{
- TRACESUBSYSTEMINITIALIZATION("classcache_init");
-
- /* create the hashtable */
-
- hashtable_create(&hashtable_classcache, CLASSCACHE_INIT_SIZE);
-
-#if defined(ENABLE_THREADS)
- /* create utf hashtable mutex */
-
- classcache_hashtable_mutex = Mutex_new();
-#endif
-
- /* everything's ok */
-
- return true;
-}
-
-/* classcache_new_loader_entry *************************************************
-
- Create a new classcache_loader_entry struct
- (internally used helper function)
-
- IN:
- loader...........the ClassLoader object
- next.............the next classcache_loader_entry
-
- RETURN VALUE:
- the new classcache_loader_entry
-
-*******************************************************************************/
-
-static classcache_loader_entry * classcache_new_loader_entry(
- classloader_t * loader,
- classcache_loader_entry * next)
-{
- classcache_loader_entry *lden;
-
- lden = NEW(classcache_loader_entry);
- lden->loader = loader;
- lden->next = next;
- CLASSCACHE_COUNT(stat_new_loader_entry);
-
- return lden;
-}
-
-/* classcache_merge_loaders ****************************************************
-
- Merge two lists of loaders into one
- (internally used helper function)
-
- IN:
- lista............first list (may be NULL)
- listb............second list (may be NULL)
-
- RETURN VALUE:
- the merged list (may be NULL)
-
- NOTE:
- The lists given as arguments are destroyed!
-
-*******************************************************************************/
-
-static classcache_loader_entry * classcache_merge_loaders(
- classcache_loader_entry * lista,
- classcache_loader_entry * listb)
-{
- classcache_loader_entry *result;
- classcache_loader_entry *ldenA;
- classcache_loader_entry *ldenB;
- classcache_loader_entry **chain;
-
- CLASSCACHE_COUNT(stat_merge_loader_entries);
-
- /* XXX This is a quadratic algorithm. If this ever
- * becomes a problem, the loader lists should be
- * stored as sorted lists and merged in linear time. */
-
- result = NULL;
- chain = &result;
-
- for (ldenA = lista; ldenA; ldenA = ldenA->next) {
-
- for (ldenB = listb; ldenB; ldenB = ldenB->next) {
- if (ldenB->loader == ldenA->loader)
- goto common_element;
- }
-
- /* this loader is only in lista */
- *chain = ldenA;
- chain = &(ldenA->next);
-
- common_element:
- /* XXX free the duplicated element */
- ;
- }
-
- /* concat listb to the result */
- *chain = listb;
-
- return result;
-}
-
-/* classcache_merge_class_entries **********************************************
-
- Merge two `classcache_class_entry`s into one.
- (internally used helper function)
-
- IN:
- en...............the classcache_name_entry containing both class entries
- clsenA...........first class entry, will receive the result
- clsenB...........second class entry
-
- PRE-CONDITION:
- Either both entries must have the same classobj, or one of them has
- classobj == NULL.
-
- NOTE:
- clsenB is freed by this function!
-
-*******************************************************************************/
-
-static void classcache_merge_class_entries(classcache_name_entry *en,
- classcache_class_entry *clsenA,
- classcache_class_entry *clsenB)
-{
-#ifdef CLASSCACHE_VERBOSE
- char logbuffer[1024];
-#endif
-
- assert(en);
- assert(clsenA);
- assert(clsenB);
- assert(!clsenA->classobj || !clsenB->classobj || clsenA->classobj == clsenB->classobj);
-
-#ifdef CLASSCACHE_VERBOSE
- sprintf(logbuffer,"classcache_merge_class_entries(%p,%p->%p,%p->%p) ",
- (void*)en,(void*)clsenA,(void*)clsenA->classobj,(void*)clsenB,(void*)clsenB->classobj);
- if (clsenA->classobj)
- utf_cat_classname(logbuffer, clsenA->classobj->name);
- if (clsenB->classobj)
- utf_cat_classname(logbuffer, clsenB->classobj->name);
- log_println(logbuffer);
-#endif
-
- CLASSCACHE_COUNT(stat_merge_class_entries);
-
- /* clsenB will be merged into clsenA */
- clsenA->loaders = classcache_merge_loaders(clsenA->loaders, clsenB->loaders);
- clsenB->loaders = NULL; /* these have been freed or reused */
-
- clsenA->constraints = classcache_merge_loaders(clsenA->constraints,
- clsenB->constraints);
- clsenB->constraints = NULL; /* these have been freed or reused */
-
- if (!clsenA->classobj)
- clsenA->classobj = clsenB->classobj;
-
- /* remove clsenB from the list of class entries */
- classcache_remove_class_entry(en, clsenB);
-}
-
-
-/* classcache_lookup_name ******************************************************
-
- Lookup a name in the first level of the cache
- (internally used helper function)
-
- IN:
- name.............the name to look up
-
- RETURN VALUE:
- a pointer to the classcache_name_entry for this name, or
- null if no entry was found.
-
-*******************************************************************************/
-
-static classcache_name_entry *classcache_lookup_name(utf *name)
-{
- classcache_name_entry *c; /* hash table element */
- u4 key; /* hashkey computed from classname */
- u4 slot; /* slot in hashtable */
-
- CLASSCACHE_COUNT(stat_lookup_name);
-
- key = CLASSCACHE_HASH(name->text, (u4) name->blength);
- slot = key & (hashtable_classcache.size - 1);
- c = hashtable_classcache.ptr[slot];
-
- /* search external hash chain for the entry */
-
- while (c) {
- /* entry found in hashtable */
- CLASSCACHE_COUNT(stat_lookup_name_entry);
-
- if (c->name == name)
- return c;
-
- c = c->hashlink; /* next element in external chain */
- }
-
- /* not found */
-
- CLASSCACHE_COUNT(stat_lookup_name_notfound);
- return NULL;
-}
-
-
-/* classcache_new_name *********************************************************
-
- Return a classcache_name_entry for the given name. The entry is created
- if it is not already in the cache.
- (internally used helper function)
-
- IN:
- name.............the name to look up / create an entry for
-
- RETURN VALUE:
- a pointer to the classcache_name_entry for this name
-
-*******************************************************************************/
-
-static classcache_name_entry *classcache_new_name(utf *name)
-{
- classcache_name_entry *c; /* hash table element */
- u4 key; /* hashkey computed from classname */
- u4 slot; /* slot in hashtable */
- u4 i;
-
- CLASSCACHE_COUNT(stat_lookup_new_name);
-
- key = CLASSCACHE_HASH(name->text, (u4) name->blength);
- slot = key & (hashtable_classcache.size - 1);
- c = hashtable_classcache.ptr[slot];
-
- /* search external hash chain for the entry */
-
- while (c) {
- /* entry found in hashtable */
- CLASSCACHE_COUNT(stat_lookup_new_name_entry);
-
- if (c->name == name)
- return c;
-
- c = c->hashlink; /* next element in external chain */
- }
-
- /* location in hashtable found, create new entry */
-
- c = NEW(classcache_name_entry);
-
- c->name = name;
- c->classes = NULL;
-
- /* insert entry into hashtable */
- c->hashlink = (classcache_name_entry *) hashtable_classcache.ptr[slot];
- CLASSCACHE_COUNTIF(c->hashlink,stat_lookup_new_name_collisions);
- hashtable_classcache.ptr[slot] = c;
-
- /* update number of hashtable-entries */
- hashtable_classcache.entries++;
- CLASSCACHE_COUNT(stat_classnames_stored);
-
- if ((hashtable_classcache.entries*2) > hashtable_classcache.size) {
- /* reorganization of hashtable */
-
- classcache_name_entry *c2;
- hashtable newhash; /* the new hashtable */
-
- CLASSCACHE_COUNT(stat_rehash_names);
-
- /* create new hashtable, double the size */
-
- hashtable_create(&newhash, hashtable_classcache.size * 2);
- newhash.entries = hashtable_classcache.entries;
-
- /* transfer elements to new hashtable */
-
- for (i = 0; i < hashtable_classcache.size; i++) {
- c2 = (classcache_name_entry *) hashtable_classcache.ptr[i];
- while (c2) {
- classcache_name_entry *nextc = c2->hashlink;
- u4 newslot =
- (CLASSCACHE_HASH(c2->name->text, (u4) c2->name->blength)) & (newhash.size - 1);
-
- c2->hashlink = (classcache_name_entry *) newhash.ptr[newslot];
- CLASSCACHE_COUNTIF(c2->hashlink,stat_rehash_names_collisions);
- newhash.ptr[newslot] = c2;
-
- c2 = nextc;
- }
- }
-
- /* dispose old table */
-
- MFREE(hashtable_classcache.ptr, void *, hashtable_classcache.size);
- hashtable_classcache = newhash;
- }
-
- return c;
-}
-
-
-/* classcache_lookup ***********************************************************
-
- Lookup a possibly loaded class
-
- IN:
- initloader.......initiating loader for resolving the class name
- classname........class name to look up
-
- RETURN VALUE:
- The return value is a pointer to the cached class object,
- or NULL, if the class is not in the cache.
-
- Note: synchronized with global tablelock
-
-*******************************************************************************/
-
-classinfo *classcache_lookup(classloader_t *initloader, utf *classname)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsen;
- classcache_loader_entry *lden;
- classinfo *cls = NULL;
-
- CLASSCACHE_LOCK();
-
- CLASSCACHE_COUNT(stat_lookup);
- en = classcache_lookup_name(classname);
-
- if (en) {
- /* iterate over all class entries */
-
- for (clsen = en->classes; clsen; clsen = clsen->next) {
- CLASSCACHE_COUNT(stat_lookup_class_entry_checked);
- /* check if this entry has been loaded by initloader */
-
- for (lden = clsen->loaders; lden; lden = lden->next) {
- CLASSCACHE_COUNT(stat_lookup_loader_checked);
- if (lden->loader == initloader) {
- /* found the loaded class entry */
-
- assert(clsen->classobj);
- cls = clsen->classobj;
- goto found;
- }
- }
- }
- }
-
- found:
- CLASSCACHE_UNLOCK();
- return cls;
-}
-
-
-/* classcache_lookup_defined ***************************************************
-
- Lookup a class with the given name and defining loader
-
- IN:
- defloader........defining loader
- classname........class name
-
- RETURN VALUE:
- The return value is a pointer to the cached class object,
- or NULL, if the class is not in the cache.
-
-*******************************************************************************/
-
-classinfo *classcache_lookup_defined(classloader_t *defloader, utf *classname)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsen;
- classinfo *cls = NULL;
-
- CLASSCACHE_LOCK();
-
- en = classcache_lookup_name(classname);
-
- if (en) {
- /* iterate over all class entries */
- for (clsen = en->classes; clsen; clsen = clsen->next) {
- if (!clsen->classobj)
- continue;
-
- /* check if this entry has been defined by defloader */
- if (clsen->classobj->classloader == defloader) {
- cls = clsen->classobj;
- goto found;
- }
- }
- }
-
- found:
- CLASSCACHE_UNLOCK();
- return cls;
-}
-
-
-/* classcache_lookup_defined_or_initiated **************************************
-
- Lookup a class that has been defined or initiated by the given loader
-
- IN:
- loader...........defining or initiating loader
- classname........class name to look up
-
- RETURN VALUE:
- The return value is a pointer to the cached class object,
- or NULL, if the class is not in the cache.
-
- Note: synchronized with global tablelock
-
-*******************************************************************************/
-
-classinfo *classcache_lookup_defined_or_initiated(classloader_t *loader,
- utf *classname)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsen;
- classcache_loader_entry *lden;
- classinfo *cls = NULL;
-
- CLASSCACHE_LOCK();
-
- en = classcache_lookup_name(classname);
-
- if (en) {
- /* iterate over all class entries */
-
- for (clsen = en->classes; clsen; clsen = clsen->next) {
-
- /* check if this entry has been defined by loader */
- if (clsen->classobj && clsen->classobj->classloader == loader) {
- cls = clsen->classobj;
- goto found;
- }
-
- /* check if this entry has been initiated by loader */
- for (lden = clsen->loaders; lden; lden = lden->next) {
- if (lden->loader == loader) {
- /* found the loaded class entry */
-
- assert(clsen->classobj);
- cls = clsen->classobj;
- goto found;
- }
- }
- }
- }
-
- found:
- CLASSCACHE_UNLOCK();
- return cls;
-}
-
-
-/* classcache_store ************************************************************
-
- Store a loaded class. If a class of the same name has already been stored
- with the same initiating loader, then the given class CLS is freed (if
- possible) and the previously stored class is returned.
-
- IN:
- initloader.......initiating loader used to load the class
- (may be NULL indicating the bootstrap loader)
- cls..............class object to cache
- mayfree..........true if CLS may be freed in case another class is
- returned
-
- RETURN VALUE:
- cls..............everything ok, the class was stored in the cache,
- other classinfo..another class with the same (initloader,name) has been
- stored earlier. CLS has been freed[1] and the earlier
- stored class is returned.
- NULL.............an exception has been thrown.
-
- Note: synchronized with global tablelock
-
- [1]...in case MAYFREE is true
-
-*******************************************************************************/
-
-classinfo *classcache_store(classloader_t *initloader, classinfo *cls,
- bool mayfree)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsen;
- classcache_class_entry *clsenB;
- classcache_loader_entry *lden;
-#ifdef CLASSCACHE_VERBOSE
- char logbuffer[1024];
-#endif
-
- assert(cls);
- assert(cls->state & CLASS_LOADED);
-
- CLASSCACHE_LOCK();
-
-#ifdef CLASSCACHE_VERBOSE
- sprintf(logbuffer,"classcache_store (%p,%d,%p=", (void*)initloader,mayfree,(void*)cls);
- utf_cat_classname(logbuffer, cls->name);
- strcat(logbuffer,")");
- log_println(logbuffer);
-#endif
-
- en = classcache_new_name(cls->name);
-
- assert(en);
-
- /* iterate over all class entries */
- for (clsen = en->classes; clsen; clsen = clsen->next) {
-
- /* check if this entry has already been loaded by initloader */
- for (lden = clsen->loaders; lden; lden = lden->next) {
- if (lden->loader == initloader) {
- if (clsen->classobj != cls) {
- /* A class with the same (initloader,name) pair has been stored already. */
- /* We free the given class and return the earlier one. */
-#ifdef CLASSCACHE_VERBOSE
- log_println("replacing %p with earlier loaded class %p",cls,clsen->classobj);
-#endif
- assert(clsen->classobj);
- if (mayfree)
- class_free(cls);
- cls = clsen->classobj;
- }
- goto return_success;
- }
- }
-
- /* {This entry has not been resolved with initloader} */
-
- /* check if initloader is constrained to this entry */
- for (lden = clsen->constraints; lden; lden = lden->next) {
- if (lden->loader == initloader) {
- /* we have to use this entry. check if it has been resolved */
- if (clsen->classobj) {
- /* check if is has already been resolved to another class */
- if (clsen->classobj != cls) {
- /* a loading constraint is violated */
- exceptions_throw_linkageerror("loading constraint violated: ", cls);
- goto return_exception;
- }
-
- /* record initloader as initiating loader */
- clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
- goto return_success;
- }
-
- /* {this is the first resolution for this entry} */
- /* record initloader as initiating loader */
- clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
-
- /* maybe we can merge this entry with another one */
- for (clsenB = en->classes; clsenB; clsenB = clsenB->next) {
- /* we dont want the entry that we have already */
- if (clsenB->classobj == cls) {
- /* this entry has the same classobj. let's merge them */
- classcache_merge_class_entries(en,clsen,clsenB);
- goto return_success;
- }
- }
-
- /* record the loaded class object */
- clsen->classobj = cls;
- CLASSCACHE_COUNT(stat_classes_stored);
-
- /* done */
- goto return_success;
- }
- }
-
- }
-
- /* {There is no class entry containing initloader as initiating
- * or constrained loader.} */
-
- /* we look for a class entry with the same classobj we want to store */
- for (clsen = en->classes; clsen; clsen = clsen->next) {
- if (clsen->classobj == cls) {
- /* this entry is about the same classobj. let's use it */
- /* check if this entry has already been loaded by initloader */
- for (lden = clsen->loaders; lden; lden = lden->next) {
- if (lden->loader == initloader)
- goto return_success;
- }
- clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
- goto return_success;
- }
- }
-
- /* create a new class entry for this class object with */
- /* initiating loader initloader */
-
- clsen = NEW(classcache_class_entry);
- clsen->classobj = cls;
- clsen->loaders = classcache_new_loader_entry(initloader, NULL);
- clsen->constraints = NULL;
-
- clsen->next = en->classes;
- en->classes = clsen;
- CLASSCACHE_COUNT(stat_classes_stored);
-
- return_success:
-#ifdef CLASSCACHE_VERBOSE
- classcache_debug_dump(stdout,cls->name);
-#endif
- CLASSCACHE_UNLOCK();
- return cls;
-
- return_exception:
- CLASSCACHE_UNLOCK();
- return NULL; /* exception */
-}
-
-/* classcache_store_unique *****************************************************
-
- Store a loaded class as loaded by the bootstrap loader. This is a wrapper
- aroung classcache_store that throws an exception if a class with the same
- name has already been loaded by the bootstrap loader.
-
- This function is used to register a few special classes during startup.
- It should not be used otherwise.
-
- IN:
- cls..............class object to cache
-
- RETURN VALUE:
- true.............everything ok, the class was stored.
- false............an exception has been thrown.
-
- Note: synchronized with global tablelock
-
-*******************************************************************************/
-
-bool classcache_store_unique(classinfo *cls)
-{
- classinfo *result;
-
- result = classcache_store(NULL,cls,false);
- if (result == NULL)
- return false;
-
- if (result != cls) {
- exceptions_throw_internalerror("class already stored in the class cache");
- return false;
- }
-
- return true;
-}
-
-/* classcache_store_defined ****************************************************
-
- Store a loaded class after it has been defined. If the class has already
- been defined by the same defining loader in another thread, free the given
- class and returned the one which has been defined earlier.
-
- IN:
- cls..............class object to store. classloader must be set
- (classloader may be NULL, for bootloader)
-
- RETURN VALUE:
- cls..............everything ok, the class was stored the cache,
- other classinfo..the class had already been defined, CLS was freed, the
- class which was defined earlier is returned,
- NULL.............an exception has been thrown.
-
-*******************************************************************************/
-
-classinfo *classcache_store_defined(classinfo *cls)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsen;
-#ifdef CLASSCACHE_VERBOSE
- char logbuffer[1024];
-#endif
-
- assert(cls);
- assert(cls->state & CLASS_LOADED);
-
- CLASSCACHE_LOCK();
-
-#ifdef CLASSCACHE_VERBOSE
- sprintf(logbuffer,"classcache_store_defined (%p,", (void*)cls->classloader);
- utf_cat_classname(logbuffer, cls->name);
- strcat(logbuffer,")");
- log_println(logbuffer);
-#endif
-
- en = classcache_new_name(cls->name);
-
- assert(en);
-
- /* iterate over all class entries */
- for (clsen = en->classes; clsen; clsen = clsen->next) {
-
- /* check if this class has been defined by the same classloader */
- if (clsen->classobj && clsen->classobj->classloader == cls->classloader) {
- /* we found an earlier definition, delete the newer one */
- /* (if it is a different classinfo) */
- if (clsen->classobj != cls) {
-#ifdef CLASSCACHE_VERBOSE
- log_println("replacing %p with earlier defined class %p",cls,clsen->classobj);
-#endif
- class_free(cls);
- cls = clsen->classobj;
- }
- goto return_success;
- }
- }
-
- /* create a new class entry for this class object */
- /* the list of initiating loaders is empty at this point */
-
- clsen = NEW(classcache_class_entry);
- clsen->classobj = cls;
- clsen->loaders = NULL;
- clsen->constraints = NULL;
-
- clsen->next = en->classes;
- en->classes = clsen;
- CLASSCACHE_COUNT(stat_classes_stored);
-
-return_success:
-#ifdef CLASSCACHE_VERBOSE
- classcache_debug_dump(stdout,cls->name);
-#endif
- CLASSCACHE_UNLOCK();
- return cls;
-}
-
-/* classcache_find_loader ******************************************************
-
- Find the class entry loaded by or constrained to a given loader
- (internally used helper function)
-
- IN:
- entry............the classcache_name_entry
- loader...........the loader to look for
-
- RETURN VALUE:
- the classcache_class_entry for the given loader, or
- NULL if no entry was found
-
-*******************************************************************************/
-
-static classcache_class_entry * classcache_find_loader(
- classcache_name_entry * entry,
- classloader_t * loader)
-{
- classcache_class_entry *clsen;
- classcache_loader_entry *lden;
-
- assert(entry);
-
- /* iterate over all class entries */
- for (clsen = entry->classes; clsen; clsen = clsen->next) {
-
- /* check if this entry has already been loaded by initloader */
- for (lden = clsen->loaders; lden; lden = lden->next) {
- if (lden->loader == loader)
- return clsen; /* found */
- }
-
- /* check if loader is constrained to this entry */
- for (lden = clsen->constraints; lden; lden = lden->next) {
- if (lden->loader == loader)
- return clsen; /* found */
- }
- }
-
- /* not found */
- return NULL;
-}
-
-/* classcache_free_class_entry *************************************************
-
- Free the memory used by a class entry
-
- IN:
- clsen............the classcache_class_entry to free
-
-*******************************************************************************/
-
-static void classcache_free_class_entry(classcache_class_entry * clsen)
-{
- classcache_loader_entry *lden;
- classcache_loader_entry *next;
-
- assert(clsen);
-
- for (lden = clsen->loaders; lden; lden = next) {
- next = lden->next;
- FREE(lden, classcache_loader_entry);
- }
- for (lden = clsen->constraints; lden; lden = next) {
- next = lden->next;
- FREE(lden, classcache_loader_entry);
- }
-
- FREE(clsen, classcache_class_entry);
-}
-
-/* classcache_remove_class_entry ***********************************************
-
- Remove a classcache_class_entry from the list of possible resolution of
- a name entry
- (internally used helper function)
-
- IN:
- entry............the classcache_name_entry
- clsen............the classcache_class_entry to remove
-
-*******************************************************************************/
-
-static void classcache_remove_class_entry(classcache_name_entry * entry,
- classcache_class_entry * clsen)
-{
- classcache_class_entry **chain;
-
- assert(entry);
- assert(clsen);
-
- chain = &(entry->classes);
- while (*chain) {
- if (*chain == clsen) {
- *chain = clsen->next;
- classcache_free_class_entry(clsen);
- return;
- }
- chain = &((*chain)->next);
- }
-}
-
-/* classcache_free_name_entry **************************************************
-
- Free the memory used by a name entry
-
- IN:
- entry............the classcache_name_entry to free
-
-*******************************************************************************/
-
-static void classcache_free_name_entry(classcache_name_entry * entry)
-{
- classcache_class_entry *clsen;
- classcache_class_entry *next;
-
- assert(entry);
-
- for (clsen = entry->classes; clsen; clsen = next) {
- next = clsen->next;
- classcache_free_class_entry(clsen);
- }
-
- FREE(entry, classcache_name_entry);
-}
-
-/* classcache_free *************************************************************
-
- Free the memory used by the class cache
-
- NOTE:
- The class cache may not be used any more after this call, except
- when it is reinitialized with classcache_init.
-
- Note: NOT synchronized!
-
-*******************************************************************************/
-
-void classcache_free(void)
-{
- u4 slot;
- classcache_name_entry *entry;
- classcache_name_entry *next;
-
- for (slot = 0; slot < hashtable_classcache.size; ++slot) {
- for (entry = (classcache_name_entry *) hashtable_classcache.ptr[slot]; entry; entry = next) {
- next = entry->hashlink;
- classcache_free_name_entry(entry);
- }
- }
-
- MFREE(hashtable_classcache.ptr, void*, hashtable_classcache.size);
- hashtable_classcache.size = 0;
- hashtable_classcache.entries = 0;
- hashtable_classcache.ptr = NULL;
-}
-
-/* classcache_add_constraint ***************************************************
-
- Add a loading constraint
-
- IN:
- a................first initiating loader
- b................second initiating loader
- classname........class name
-
- RETURN VALUE:
- true.............everything ok, the constraint has been added,
- false............an exception has been thrown.
-
- Note: synchronized with global tablelock
-
-*******************************************************************************/
-
-#if defined(ENABLE_VERIFIER)
-bool classcache_add_constraint(classloader_t * a,
- classloader_t * b,
- utf * classname)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsenA;
- classcache_class_entry *clsenB;
-
- assert(classname);
-
-#ifdef CLASSCACHE_VERBOSE
- log_start();
- log_print("classcache_add_constraint(%p,%p,", (void *) a, (void *) b);
- utf_fprint_printable_ascii_classname(stdout, classname);
- log_print(")\n");
- log_finish();
-#endif
-
- /* a constraint with a == b is trivially satisfied */
- if (a == b) {
- CLASSCACHE_COUNT(stat_trivial_constraints);
- return true;
- }
-
- CLASSCACHE_LOCK();
-
- en = classcache_new_name(classname);
-
- assert(en);
- CLASSCACHE_COUNT(stat_nontriv_constraints);
-
- /* find the entry loaded by / constrained to each loader */
- clsenA = classcache_find_loader(en, a);
- clsenB = classcache_find_loader(en, b);
-
- if (clsenA && clsenB) {
- /* { both loaders have corresponding entries } */
- CLASSCACHE_COUNT(stat_nontriv_constraints_both);
-
- /* if the entries are the same, the constraint is already recorded */
- if (clsenA == clsenB)
- goto return_success;
-
- /* check if the entries can be merged */
- if (clsenA->classobj && clsenB->classobj
- && clsenA->classobj != clsenB->classobj) {
- /* no, the constraint is violated */
- exceptions_throw_linkageerror("loading constraint violated: ",
- clsenA->classobj);
- goto return_exception;
- }
-
- /* yes, merge the entries */
- classcache_merge_class_entries(en,clsenA,clsenB);
- CLASSCACHE_COUNT(stat_nontriv_constraints_merged);
- }
- else {
- /* { at most one of the loaders has a corresponding entry } */
-
- /* set clsenA to the single class entry we have */
- if (!clsenA)
- clsenA = clsenB;
-
- if (!clsenA) {
- /* { no loader has a corresponding entry } */
- CLASSCACHE_COUNT(stat_nontriv_constraints_none);
-
- /* create a new class entry with the constraint (a,b,en->name) */
- clsenA = NEW(classcache_class_entry);
- clsenA->classobj = NULL;
- clsenA->loaders = NULL;
- clsenA->constraints = classcache_new_loader_entry(b, NULL);
- clsenA->constraints = classcache_new_loader_entry(a, clsenA->constraints);
-
- clsenA->next = en->classes;
- en->classes = clsenA;
- }
- else {
- CLASSCACHE_COUNT(stat_nontriv_constraints_one);
-
- /* make b the loader that has no corresponding entry */
- if (clsenB)
- b = a;
-
- /* loader b must be added to entry clsenA */
- clsenA->constraints = classcache_new_loader_entry(b, clsenA->constraints);
- }
- }
-
- return_success:
- CLASSCACHE_UNLOCK();
- return true;
-
- return_exception:
- CLASSCACHE_UNLOCK();
- return false; /* exception */
-}
-#endif /* defined(ENABLE_VERIFIER) */
-
-/* classcache_add_constraints_for_params ***************************************
-
- Add loading constraints for the parameters and return type of
- the given method.
-
- IN:
- a................first initiating loader
- b................second initiating loader
- m................methodinfo
-
- RETURN VALUE:
- true.............everything ok, the constraints have been added,
- false............an exception has been thrown.
-
- Note: synchronized with global tablelock
-
-*******************************************************************************/
-
-#if defined(ENABLE_VERIFIER)
-bool classcache_add_constraints_for_params(classloader_t * a,
- classloader_t * b,
- methodinfo *m)
-{
- methoddesc *md;
- typedesc *td;
- s4 i;
-
- /* a constraint with a == b is trivially satisfied */
-
- if (a == b) {
- return true;
- }
-
- /* get the parsed descriptor */
-
- assert(m);
- md = m->parseddesc;
- assert(md);
-
- /* constrain the return type */
-
- if (md->returntype.type == TYPE_ADR) {
- if (!classcache_add_constraint(a, b, md->returntype.classref->name))
- return false; /* exception */
- }
-
- /* constrain each reference type used in the parameters */
-
- td = md->paramtypes;
- i = md->paramcount;
- for (; i--; td++) {
- if (td->type != TYPE_ADR)
- continue;
-
- if (!classcache_add_constraint(a, b, td->classref->name))
- return false; /* exception */
- }
-
- /* everything ok */
- return true;
-}
-#endif /* defined(ENABLE_VERIFIER) */
-
-
-/* classcache_number_of_loaded_classes *****************************************
-
- Counts the number of loaded classes and returns it.
-
- Note: This function assumes that the CLASSCACHE_LOCK is held by the
- caller!
-
-*******************************************************************************/
-
-static s4 classcache_number_of_loaded_classes(void)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsen;
- s4 number;
- s4 i;
-
- /* initialize class counter */
-
- number = 0;
-
- for (i = 0; i < hashtable_classcache.size; i++) {
- /* iterate over hashlink */
-
- for (en = hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
- /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
-
- if (en->name->text[0] == '$')
- continue;
-
- /* iterate over classes with same name */
-
- for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
- /* get only loaded classes */
-
- if (clsen->classobj != NULL)
- number++;
- }
- }
- }
-
- return number;
-}
-
-
-/* classcache_get_loaded_class_count *******************************************
-
- Counts the number of loaded classes and returns it.
-
-*******************************************************************************/
-
-s4 classcache_get_loaded_class_count(void)
-{
- s4 count;
-
- CLASSCACHE_LOCK();
-
- count = classcache_number_of_loaded_classes();
-
- CLASSCACHE_UNLOCK();
-
- return count;
-}
-
-
-/* classcache_get_loaded_classes ***********************************************
-
- Returns an array of all loaded classes as array. The array is
- allocaed on the Java heap.
-
-*******************************************************************************/
-
-#if defined(ENABLE_JVMTI)
-void classcache_get_loaded_classes(s4 *class_count_ptr,
- classinfo ***classes_ptr)
-{
- classinfo **classes;
- s4 class_count;
- classcache_name_entry *en;
- classcache_class_entry *clsen;
- s4 i;
- s4 j;
-
- CLASSCACHE_LOCK();
-
- /* get the number of loaded classes and allocate the array */
-
- class_count = classcache_number_of_loaded_classes();
-
- classes = GCMNEW(classinfo*, class_count);
-
- /* look in every slot of the hashtable */
-
- for (i = 0, j = 0; i < hashtable_classcache.size; i++) {
- /* iterate over hashlink */
-
- for (en = hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
- /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
-
- if (en->name->text[0] == '$')
- continue;
-
- /* iterate over classes with same name */
-
- for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
- /* get only loaded classes */
-
- if (clsen->classobj != NULL) {
- classes[j] = clsen->classobj;
- j++;
- }
- }
- }
- }
-
- /* pass the return values */
-
- *class_count_ptr = class_count;
- *classes_ptr = classes;
-
- CLASSCACHE_UNLOCK();
-}
-#endif /* defined(ENABLE_JVMTI) */
-
-
-/* classcache_foreach_loaded_class *********************************************
-
- Calls the given function for each loaded class.
-
-*******************************************************************************/
-
-void classcache_foreach_loaded_class(classcache_foreach_functionptr_t func,
- void *data)
-{
- classcache_name_entry *en;
- classcache_class_entry *clsen;
- s4 i;
-
- CLASSCACHE_LOCK();
-
- /* look in every slot of the hashtable */
-
- for (i = 0; i < hashtable_classcache.size; i++) {
- /* iterate over hashlink */
-
- for (en = hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
- /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
-
- if (en->name->text[0] == '$')
- continue;
-
- /* iterate over classes with same name */
-
- for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
- /* get only loaded classes */
-
- if (clsen->classobj != NULL) {
- (*func)(clsen->classobj, data);
- }
- }
- }
- }
-
- CLASSCACHE_UNLOCK();
-}
-
-
-/*============================================================================*/
-/* DEBUG DUMPS */
-/*============================================================================*/
-
-/* classcache_debug_dump *******************************************************
-
- Print the contents of the loaded class cache to a stream
-
- IN:
- file.............output stream
- only.............if != NULL, only print entries for this name
- (Currently we print also the rest of the hash chain to
- get a feel for the average length of hash chains.)
-
- Note: synchronized with global tablelock
-
-*******************************************************************************/
-
-#ifndef NDEBUG
-void classcache_debug_dump(FILE * file,utf *only)
-{
- classcache_name_entry *c;
- classcache_class_entry *clsen;
- classcache_loader_entry *lden;
- u4 slot;
-
- CLASSCACHE_LOCK();
-
- log_println("=== [loaded class cache] =====================================");
- log_println("hash size : %d", (int) hashtable_classcache.size);
- log_println("hash entries: %d", (int) hashtable_classcache.entries);
- log_println("");
-
- if (only) {
- c = classcache_lookup_name(only);
- slot = 0; /* avoid compiler warning */
- goto dump_it;
- }
-
- for (slot = 0; slot < hashtable_classcache.size; ++slot) {
- c = (classcache_name_entry *) hashtable_classcache.ptr[slot];
-
-dump_it:
- for (; c; c = c->hashlink) {
- utf_fprint_printable_ascii_classname(file, c->name);
- fprintf(file, "\n");
-
- /* iterate over all class entries */
- for (clsen = c->classes; clsen; clsen = clsen->next) {
- if (clsen->classobj) {
- log_println(" loaded %p", (void *) clsen->classobj);
- }
- else {
- log_println(" unresolved");
- }
-
- log_start();
- log_print(" loaders: ");
- for (lden = clsen->loaders; lden; lden = lden->next) {
- log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
- }
- log_finish();
-
- log_start();
- log_print(" constraints: ");
- for (lden = clsen->constraints; lden; lden = lden->next) {
- log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
- }
- log_finish();
- }
- }
-
- if (only)
- break;
- }
- fprintf(file, "\n==============================================================\n\n");
-
- CLASSCACHE_UNLOCK();
-}
-#endif /* NDEBUG */
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/classcache.cpp - loaded class cache and loading constraints
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "threads/lock.hpp"
+#include "threads/mutex.hpp"
+
+#include "toolbox/hashtable.h"
+#include "toolbox/logging.h"
+
+#include "vm/classcache.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/options.h"
+#include "vm/utf8.h"
+
+
+/*************************************************************************
+
+ Class Cache
+
+ The classcache has two functions:
+
+ 1) caching the resolution of class references
+ 2) storing and checking loading constraints
+
+ We will use the following terms in this description:
+
+ N a class name: a utf string
+ (N,L) a class reference with initiating loader L and class name N
+ C a class (object): the result of resolving a reference (N,L)
+ We will write resultion as
+ C = *(N,L)
+ (N,L1,L2) a loading constraint indicating that (N,L1) and (N,L2) must
+ resolve to the same class C. So (N,L1,L2) means
+ *(N,L1) = *(N,L2)
+
+ The functions of the classcache require:
+
+ 1) a mapping (N,L) |--> C for looking up prior resolution results.
+ 2) storing the current set of loading constraints { (N,L1,L2) }
+
+ These functions can be rearranged like that:
+
+ a mapping N |--> (a mapping L |--> C or NULL,
+ a set of constraints {(L1,L2)})
+
+ Thus we can treat the mapping and constraints for each name N
+ separately. The implementation does this by keeping a hash table
+ mapping a name N to a `classcache_name_entry` which contains all
+ info with respect to N.
+
+ For a class name N we can define an equivalence relation ~N~ on
+ class loaders:
+
+ L1 ~N~ L2 <==> *(N,L1) = *(N,L2)
+
+ A loading constraint (N,L1,L2) implies L1 ~N~ L2.
+
+ Also, if two references (N,L1) and (N,L2) resolve to the same class C
+ we have L1 ~N~ L2 because class loaders are required to return
+ consistent resolutions for a name N [XXX].
+
+ A `classcache_name_entry` keeps a set of tuples { (Cx,IL,CL) },
+ where
+ Cx...is a class C or NULL
+ IL...is the set of initiating loaders
+ CL...is the set of constrained loaders
+
+ Such a tuple is called `classcache_class_entry` in the source code.
+
+ The following holds for each tuple (Cx,IL,CL):
+
+ . (Cx is NULL) implies IL = {}.
+
+ . If Cx is a class, IL is the set of loaders that have been
+ recorded as initiating loaders for Cx. IL may be the
+ empty set {} in case Cx has already been defined but no
+ initiating loader has been recorded, yet.
+
+ . (IL u CL) is a subset of an equivalence class of ~N~.
+
+ (This means that all loaders in IL and CL must resolve
+ the name N to the same class.)
+
+ The following holds for the set of tuples { (Cx,IL,CL) }:
+
+ . For a given class C there is at most one tuple with Cx = C
+ in the set. (There may be an arbitrary number of tuples
+ with Cx = NULL, however.)
+
+ . For a given loader L there is at most one tuple with
+ L in (IL u CL).
+
+ The implementation stores sets of loaders as linked lists of
+ `classcache_loader_entry`s.
+
+ Comments about manipulating the classcache can be found in the
+ individual functions below.
+
+*************************************************************************/
+
+
+/* initial number of slots in the classcache hash table */
+#define CLASSCACHE_INIT_SIZE 2048
+
+/*============================================================================*/
+/* DEBUG HELPERS */
+/*============================================================================*/
+
+/* #define CLASSCACHE_VERBOSE */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/*============================================================================*/
+/* STATISTICS */
+/*============================================================================*/
+
+/*#define CLASSCACHE_STATS*/
+
+#ifdef CLASSCACHE_STATS
+static int stat_classnames_stored = 0;
+static int stat_classes_stored = 0;
+static int stat_trivial_constraints = 0;
+static int stat_nontriv_constraints = 0;
+static int stat_nontriv_constraints_both = 0;
+static int stat_nontriv_constraints_merged = 0;
+static int stat_nontriv_constraints_one = 0;
+static int stat_nontriv_constraints_none = 0;
+static int stat_new_loader_entry = 0;
+static int stat_merge_class_entries = 0;
+static int stat_merge_loader_entries = 0;
+static int stat_lookup = 0;
+static int stat_lookup_class_entry_checked = 0;
+static int stat_lookup_loader_checked = 0;
+static int stat_lookup_name = 0;
+static int stat_lookup_name_entry = 0;
+static int stat_lookup_name_notfound = 0;
+static int stat_lookup_new_name = 0;
+static int stat_lookup_new_name_entry = 0;
+static int stat_lookup_new_name_collisions = 0;
+static int stat_rehash_names = 0;
+static int stat_rehash_names_collisions = 0;
+
+#define CLASSCACHE_COUNT(cnt) (cnt)++
+#define CLASSCACHE_COUNTIF(cond,cnt) do{if(cond) (cnt)++;} while(0)
+
+void classcache_print_statistics(FILE *file) {
+ fprintf(file,"classnames stored : %8d\n",stat_classnames_stored);
+ fprintf(file,"classes stored : %8d\n",stat_classes_stored);
+ fprintf(file,"trivial constraints : %8d\n",stat_trivial_constraints);
+ fprintf(file,"non-triv constraints: %8d\n",stat_nontriv_constraints);
+ fprintf(file," both loaders rec.: %8d\n",stat_nontriv_constraints_both);
+ fprintf(file," merged : %8d\n",stat_nontriv_constraints_merged);
+ fprintf(file," one loader rec. : %8d\n",stat_nontriv_constraints_one);
+ fprintf(file," no loaders rec. : %8d\n",stat_nontriv_constraints_none);
+ fprintf(file,"new loader entries : %8d\n",stat_new_loader_entry);
+ fprintf(file,"merge class entries : %8d\n",stat_merge_class_entries);
+ fprintf(file,"merge loader entries: %8d\n",stat_merge_loader_entries);
+ fprintf(file,"lookups : %8d\n",stat_lookup);
+ fprintf(file," class entries ckd: %8d\n",stat_lookup_class_entry_checked);
+ fprintf(file," loader checked : %8d\n",stat_lookup_loader_checked);
+ fprintf(file,"lookup name : %8d\n",stat_lookup_name);
+ fprintf(file," entries checked : %8d\n",stat_lookup_name_entry);
+ fprintf(file," not found : %8d\n",stat_lookup_name_notfound);
+ fprintf(file,"lookup (new) name : %8d\n",stat_lookup_new_name);
+ fprintf(file," entries checked : %8d\n",stat_lookup_new_name_entry);
+ fprintf(file," new collisions : %8d\n",stat_lookup_new_name_collisions);
+ fprintf(file,"names rehashed : %8d times\n",stat_rehash_names);
+ fprintf(file," collisions : %8d\n",stat_rehash_names_collisions);
+}
+#else
+#define CLASSCACHE_COUNT(cnt)
+#define CLASSCACHE_COUNTIF(cond,cnt)
+#endif
+
+/*============================================================================*/
+/* THREAD-SAFE LOCKING */
+/*============================================================================*/
+
+ /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+ /* CAUTION: The static functions below are */
+ /* NOT synchronized! */
+ /*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+#if defined(ENABLE_THREADS)
+# define CLASSCACHE_LOCK() classcache_hashtable_mutex->lock();
+# define CLASSCACHE_UNLOCK() classcache_hashtable_mutex->unlock();
+#else
+# define CLASSCACHE_LOCK()
+# define CLASSCACHE_UNLOCK()
+#endif
+
+/*============================================================================*/
+/* GLOBAL VARIABLES */
+/*============================================================================*/
+
+hashtable hashtable_classcache;
+
+#if defined(ENABLE_THREADS)
+static Mutex *classcache_hashtable_mutex;
+#endif
+
+
+/*============================================================================*/
+/* */
+/*============================================================================*/
+
+/* prototypes */
+
+static void classcache_free_class_entry(classcache_class_entry *clsen);
+static void classcache_remove_class_entry(classcache_name_entry *en,
+ classcache_class_entry *clsen);
+
+/* hash function to use */
+
+#define CLASSCACHE_HASH utf_full_hashkey
+
+/* classcache_init *************************************************************
+
+ Initialize the class cache
+
+ Note: NOT synchronized!
+
+*******************************************************************************/
+
+bool classcache_init(void)
+{
+ TRACESUBSYSTEMINITIALIZATION("classcache_init");
+
+ /* create the hashtable */
+
+ hashtable_create(&hashtable_classcache, CLASSCACHE_INIT_SIZE);
+
+#if defined(ENABLE_THREADS)
+ /* create utf hashtable mutex */
+
+ classcache_hashtable_mutex = new Mutex();
+#endif
+
+ /* everything's ok */
+
+ return true;
+}
+
+/* classcache_new_loader_entry *************************************************
+
+ Create a new classcache_loader_entry struct
+ (internally used helper function)
+
+ IN:
+ loader...........the ClassLoader object
+ next.............the next classcache_loader_entry
+
+ RETURN VALUE:
+ the new classcache_loader_entry
+
+*******************************************************************************/
+
+static classcache_loader_entry * classcache_new_loader_entry(
+ classloader_t * loader,
+ classcache_loader_entry * next)
+{
+ classcache_loader_entry *lden;
+
+ lden = NEW(classcache_loader_entry);
+ lden->loader = loader;
+ lden->next = next;
+ CLASSCACHE_COUNT(stat_new_loader_entry);
+
+ return lden;
+}
+
+/* classcache_merge_loaders ****************************************************
+
+ Merge two lists of loaders into one
+ (internally used helper function)
+
+ IN:
+ lista............first list (may be NULL)
+ listb............second list (may be NULL)
+
+ RETURN VALUE:
+ the merged list (may be NULL)
+
+ NOTE:
+ The lists given as arguments are destroyed!
+
+*******************************************************************************/
+
+static classcache_loader_entry * classcache_merge_loaders(
+ classcache_loader_entry * lista,
+ classcache_loader_entry * listb)
+{
+ classcache_loader_entry *result;
+ classcache_loader_entry *ldenA;
+ classcache_loader_entry *ldenB;
+ classcache_loader_entry **chain;
+
+ CLASSCACHE_COUNT(stat_merge_loader_entries);
+
+ /* XXX This is a quadratic algorithm. If this ever
+ * becomes a problem, the loader lists should be
+ * stored as sorted lists and merged in linear time. */
+
+ result = NULL;
+ chain = &result;
+
+ for (ldenA = lista; ldenA; ldenA = ldenA->next) {
+
+ for (ldenB = listb; ldenB; ldenB = ldenB->next) {
+ if (ldenB->loader == ldenA->loader)
+ goto common_element;
+ }
+
+ /* this loader is only in lista */
+ *chain = ldenA;
+ chain = &(ldenA->next);
+
+ common_element:
+ /* XXX free the duplicated element */
+ ;
+ }
+
+ /* concat listb to the result */
+ *chain = listb;
+
+ return result;
+}
+
+/* classcache_merge_class_entries **********************************************
+
+ Merge two `classcache_class_entry`s into one.
+ (internally used helper function)
+
+ IN:
+ en...............the classcache_name_entry containing both class entries
+ clsenA...........first class entry, will receive the result
+ clsenB...........second class entry
+
+ PRE-CONDITION:
+ Either both entries must have the same classobj, or one of them has
+ classobj == NULL.
+
+ NOTE:
+ clsenB is freed by this function!
+
+*******************************************************************************/
+
+static void classcache_merge_class_entries(classcache_name_entry *en,
+ classcache_class_entry *clsenA,
+ classcache_class_entry *clsenB)
+{
+#ifdef CLASSCACHE_VERBOSE
+ char logbuffer[1024];
+#endif
+
+ assert(en);
+ assert(clsenA);
+ assert(clsenB);
+ assert(!clsenA->classobj || !clsenB->classobj || clsenA->classobj == clsenB->classobj);
+
+#ifdef CLASSCACHE_VERBOSE
+ sprintf(logbuffer,"classcache_merge_class_entries(%p,%p->%p,%p->%p) ",
+ (void*)en,(void*)clsenA,(void*)clsenA->classobj,(void*)clsenB,(void*)clsenB->classobj);
+ if (clsenA->classobj)
+ utf_cat_classname(logbuffer, clsenA->classobj->name);
+ if (clsenB->classobj)
+ utf_cat_classname(logbuffer, clsenB->classobj->name);
+ log_println(logbuffer);
+#endif
+
+ CLASSCACHE_COUNT(stat_merge_class_entries);
+
+ /* clsenB will be merged into clsenA */
+ clsenA->loaders = classcache_merge_loaders(clsenA->loaders, clsenB->loaders);
+ clsenB->loaders = NULL; /* these have been freed or reused */
+
+ clsenA->constraints = classcache_merge_loaders(clsenA->constraints,
+ clsenB->constraints);
+ clsenB->constraints = NULL; /* these have been freed or reused */
+
+ if (!clsenA->classobj)
+ clsenA->classobj = clsenB->classobj;
+
+ /* remove clsenB from the list of class entries */
+ classcache_remove_class_entry(en, clsenB);
+}
+
+
+/* classcache_lookup_name ******************************************************
+
+ Lookup a name in the first level of the cache
+ (internally used helper function)
+
+ IN:
+ name.............the name to look up
+
+ RETURN VALUE:
+ a pointer to the classcache_name_entry for this name, or
+ null if no entry was found.
+
+*******************************************************************************/
+
+static classcache_name_entry *classcache_lookup_name(utf *name)
+{
+ classcache_name_entry *c; /* hash table element */
+ u4 key; /* hashkey computed from classname */
+ u4 slot; /* slot in hashtable */
+
+ CLASSCACHE_COUNT(stat_lookup_name);
+
+ key = CLASSCACHE_HASH(name->text, (u4) name->blength);
+ slot = key & (hashtable_classcache.size - 1);
+ c = (classcache_name_entry*) hashtable_classcache.ptr[slot];
+
+ /* search external hash chain for the entry */
+
+ while (c) {
+ /* entry found in hashtable */
+ CLASSCACHE_COUNT(stat_lookup_name_entry);
+
+ if (c->name == name)
+ return c;
+
+ c = c->hashlink; /* next element in external chain */
+ }
+
+ /* not found */
+
+ CLASSCACHE_COUNT(stat_lookup_name_notfound);
+ return NULL;
+}
+
+
+/* classcache_new_name *********************************************************
+
+ Return a classcache_name_entry for the given name. The entry is created
+ if it is not already in the cache.
+ (internally used helper function)
+
+ IN:
+ name.............the name to look up / create an entry for
+
+ RETURN VALUE:
+ a pointer to the classcache_name_entry for this name
+
+*******************************************************************************/
+
+static classcache_name_entry *classcache_new_name(utf *name)
+{
+ classcache_name_entry *c; /* hash table element */
+ u4 key; /* hashkey computed from classname */
+ u4 slot; /* slot in hashtable */
+ u4 i;
+
+ CLASSCACHE_COUNT(stat_lookup_new_name);
+
+ key = CLASSCACHE_HASH(name->text, (u4) name->blength);
+ slot = key & (hashtable_classcache.size - 1);
+ c = (classcache_name_entry*) hashtable_classcache.ptr[slot];
+
+ /* search external hash chain for the entry */
+
+ while (c) {
+ /* entry found in hashtable */
+ CLASSCACHE_COUNT(stat_lookup_new_name_entry);
+
+ if (c->name == name)
+ return c;
+
+ c = c->hashlink; /* next element in external chain */
+ }
+
+ /* location in hashtable found, create new entry */
+
+ c = NEW(classcache_name_entry);
+
+ c->name = name;
+ c->classes = NULL;
+
+ /* insert entry into hashtable */
+ c->hashlink = (classcache_name_entry *) hashtable_classcache.ptr[slot];
+ CLASSCACHE_COUNTIF(c->hashlink,stat_lookup_new_name_collisions);
+ hashtable_classcache.ptr[slot] = c;
+
+ /* update number of hashtable-entries */
+ hashtable_classcache.entries++;
+ CLASSCACHE_COUNT(stat_classnames_stored);
+
+ if ((hashtable_classcache.entries*2) > hashtable_classcache.size) {
+ /* reorganization of hashtable */
+
+ classcache_name_entry *c2;
+ hashtable newhash; /* the new hashtable */
+
+ CLASSCACHE_COUNT(stat_rehash_names);
+
+ /* create new hashtable, double the size */
+
+ hashtable_create(&newhash, hashtable_classcache.size * 2);
+ newhash.entries = hashtable_classcache.entries;
+
+ /* transfer elements to new hashtable */
+
+ for (i = 0; i < hashtable_classcache.size; i++) {
+ c2 = (classcache_name_entry *) hashtable_classcache.ptr[i];
+ while (c2) {
+ classcache_name_entry *nextc = c2->hashlink;
+ u4 newslot =
+ (CLASSCACHE_HASH(c2->name->text, (u4) c2->name->blength)) & (newhash.size - 1);
+
+ c2->hashlink = (classcache_name_entry *) newhash.ptr[newslot];
+ CLASSCACHE_COUNTIF(c2->hashlink,stat_rehash_names_collisions);
+ newhash.ptr[newslot] = c2;
+
+ c2 = nextc;
+ }
+ }
+
+ /* dispose old table */
+
+ MFREE(hashtable_classcache.ptr, void *, hashtable_classcache.size);
+ hashtable_classcache = newhash;
+ }
+
+ return c;
+}
+
+
+/* classcache_lookup ***********************************************************
+
+ Lookup a possibly loaded class
+
+ IN:
+ initloader.......initiating loader for resolving the class name
+ classname........class name to look up
+
+ RETURN VALUE:
+ The return value is a pointer to the cached class object,
+ or NULL, if the class is not in the cache.
+
+ Note: synchronized with global tablelock
+
+*******************************************************************************/
+
+classinfo *classcache_lookup(classloader_t *initloader, utf *classname)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+ classcache_loader_entry *lden;
+ classinfo *cls = NULL;
+
+ CLASSCACHE_LOCK();
+
+ CLASSCACHE_COUNT(stat_lookup);
+ en = classcache_lookup_name(classname);
+
+ if (en) {
+ /* iterate over all class entries */
+
+ for (clsen = en->classes; clsen; clsen = clsen->next) {
+ CLASSCACHE_COUNT(stat_lookup_class_entry_checked);
+ /* check if this entry has been loaded by initloader */
+
+ for (lden = clsen->loaders; lden; lden = lden->next) {
+ CLASSCACHE_COUNT(stat_lookup_loader_checked);
+ if (lden->loader == initloader) {
+ /* found the loaded class entry */
+
+ assert(clsen->classobj);
+ cls = clsen->classobj;
+ goto found;
+ }
+ }
+ }
+ }
+
+ found:
+ CLASSCACHE_UNLOCK();
+ return cls;
+}
+
+
+/* classcache_lookup_defined ***************************************************
+
+ Lookup a class with the given name and defining loader
+
+ IN:
+ defloader........defining loader
+ classname........class name
+
+ RETURN VALUE:
+ The return value is a pointer to the cached class object,
+ or NULL, if the class is not in the cache.
+
+*******************************************************************************/
+
+classinfo *classcache_lookup_defined(classloader_t *defloader, utf *classname)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+ classinfo *cls = NULL;
+
+ CLASSCACHE_LOCK();
+
+ en = classcache_lookup_name(classname);
+
+ if (en) {
+ /* iterate over all class entries */
+ for (clsen = en->classes; clsen; clsen = clsen->next) {
+ if (!clsen->classobj)
+ continue;
+
+ /* check if this entry has been defined by defloader */
+ if (clsen->classobj->classloader == defloader) {
+ cls = clsen->classobj;
+ goto found;
+ }
+ }
+ }
+
+ found:
+ CLASSCACHE_UNLOCK();
+ return cls;
+}
+
+
+/* classcache_lookup_defined_or_initiated **************************************
+
+ Lookup a class that has been defined or initiated by the given loader
+
+ IN:
+ loader...........defining or initiating loader
+ classname........class name to look up
+
+ RETURN VALUE:
+ The return value is a pointer to the cached class object,
+ or NULL, if the class is not in the cache.
+
+ Note: synchronized with global tablelock
+
+*******************************************************************************/
+
+classinfo *classcache_lookup_defined_or_initiated(classloader_t *loader,
+ utf *classname)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+ classcache_loader_entry *lden;
+ classinfo *cls = NULL;
+
+ CLASSCACHE_LOCK();
+
+ en = classcache_lookup_name(classname);
+
+ if (en) {
+ /* iterate over all class entries */
+
+ for (clsen = en->classes; clsen; clsen = clsen->next) {
+
+ /* check if this entry has been defined by loader */
+ if (clsen->classobj && clsen->classobj->classloader == loader) {
+ cls = clsen->classobj;
+ goto found;
+ }
+
+ /* check if this entry has been initiated by loader */
+ for (lden = clsen->loaders; lden; lden = lden->next) {
+ if (lden->loader == loader) {
+ /* found the loaded class entry */
+
+ assert(clsen->classobj);
+ cls = clsen->classobj;
+ goto found;
+ }
+ }
+ }
+ }
+
+ found:
+ CLASSCACHE_UNLOCK();
+ return cls;
+}
+
+
+/* classcache_store ************************************************************
+
+ Store a loaded class. If a class of the same name has already been stored
+ with the same initiating loader, then the given class CLS is freed (if
+ possible) and the previously stored class is returned.
+
+ IN:
+ initloader.......initiating loader used to load the class
+ (may be NULL indicating the bootstrap loader)
+ cls..............class object to cache
+ mayfree..........true if CLS may be freed in case another class is
+ returned
+
+ RETURN VALUE:
+ cls..............everything ok, the class was stored in the cache,
+ other classinfo..another class with the same (initloader,name) has been
+ stored earlier. CLS has been freed[1] and the earlier
+ stored class is returned.
+ NULL.............an exception has been thrown.
+
+ Note: synchronized with global tablelock
+
+ [1]...in case MAYFREE is true
+
+*******************************************************************************/
+
+classinfo *classcache_store(classloader_t *initloader, classinfo *cls,
+ bool mayfree)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+ classcache_class_entry *clsenB;
+ classcache_loader_entry *lden;
+#ifdef CLASSCACHE_VERBOSE
+ char logbuffer[1024];
+#endif
+
+ assert(cls);
+ assert(cls->state & CLASS_LOADED);
+
+ CLASSCACHE_LOCK();
+
+#ifdef CLASSCACHE_VERBOSE
+ sprintf(logbuffer,"classcache_store (%p,%d,%p=", (void*)initloader,mayfree,(void*)cls);
+ utf_cat_classname(logbuffer, cls->name);
+ strcat(logbuffer,")");
+ log_println(logbuffer);
+#endif
+
+ en = classcache_new_name(cls->name);
+
+ assert(en);
+
+ /* iterate over all class entries */
+ for (clsen = en->classes; clsen; clsen = clsen->next) {
+
+ /* check if this entry has already been loaded by initloader */
+ for (lden = clsen->loaders; lden; lden = lden->next) {
+ if (lden->loader == initloader) {
+ if (clsen->classobj != cls) {
+ /* A class with the same (initloader,name) pair has been stored already. */
+ /* We free the given class and return the earlier one. */
+#ifdef CLASSCACHE_VERBOSE
+ log_println("replacing %p with earlier loaded class %p",cls,clsen->classobj);
+#endif
+ assert(clsen->classobj);
+ if (mayfree)
+ class_free(cls);
+ cls = clsen->classobj;
+ }
+ goto return_success;
+ }
+ }
+
+ /* {This entry has not been resolved with initloader} */
+
+ /* check if initloader is constrained to this entry */
+ for (lden = clsen->constraints; lden; lden = lden->next) {
+ if (lden->loader == initloader) {
+ /* we have to use this entry. check if it has been resolved */
+ if (clsen->classobj) {
+ /* check if is has already been resolved to another class */
+ if (clsen->classobj != cls) {
+ /* a loading constraint is violated */
+ exceptions_throw_linkageerror("loading constraint violated: ", cls);
+ goto return_exception;
+ }
+
+ /* record initloader as initiating loader */
+ clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
+ goto return_success;
+ }
+
+ /* {this is the first resolution for this entry} */
+ /* record initloader as initiating loader */
+ clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
+
+ /* maybe we can merge this entry with another one */
+ for (clsenB = en->classes; clsenB; clsenB = clsenB->next) {
+ /* we dont want the entry that we have already */
+ if (clsenB->classobj == cls) {
+ /* this entry has the same classobj. let's merge them */
+ classcache_merge_class_entries(en,clsen,clsenB);
+ goto return_success;
+ }
+ }
+
+ /* record the loaded class object */
+ clsen->classobj = cls;
+ CLASSCACHE_COUNT(stat_classes_stored);
+
+ /* done */
+ goto return_success;
+ }
+ }
+
+ }
+
+ /* {There is no class entry containing initloader as initiating
+ * or constrained loader.} */
+
+ /* we look for a class entry with the same classobj we want to store */
+ for (clsen = en->classes; clsen; clsen = clsen->next) {
+ if (clsen->classobj == cls) {
+ /* this entry is about the same classobj. let's use it */
+ /* check if this entry has already been loaded by initloader */
+ for (lden = clsen->loaders; lden; lden = lden->next) {
+ if (lden->loader == initloader)
+ goto return_success;
+ }
+ clsen->loaders = classcache_new_loader_entry(initloader, clsen->loaders);
+ goto return_success;
+ }
+ }
+
+ /* create a new class entry for this class object with */
+ /* initiating loader initloader */
+
+ clsen = NEW(classcache_class_entry);
+ clsen->classobj = cls;
+ clsen->loaders = classcache_new_loader_entry(initloader, NULL);
+ clsen->constraints = NULL;
+
+ clsen->next = en->classes;
+ en->classes = clsen;
+ CLASSCACHE_COUNT(stat_classes_stored);
+
+ return_success:
+#ifdef CLASSCACHE_VERBOSE
+ classcache_debug_dump(stdout,cls->name);
+#endif
+ CLASSCACHE_UNLOCK();
+ return cls;
+
+ return_exception:
+ CLASSCACHE_UNLOCK();
+ return NULL; /* exception */
+}
+
+/* classcache_store_unique *****************************************************
+
+ Store a loaded class as loaded by the bootstrap loader. This is a wrapper
+ aroung classcache_store that throws an exception if a class with the same
+ name has already been loaded by the bootstrap loader.
+
+ This function is used to register a few special classes during startup.
+ It should not be used otherwise.
+
+ IN:
+ cls..............class object to cache
+
+ RETURN VALUE:
+ true.............everything ok, the class was stored.
+ false............an exception has been thrown.
+
+ Note: synchronized with global tablelock
+
+*******************************************************************************/
+
+bool classcache_store_unique(classinfo *cls)
+{
+ classinfo *result;
+
+ result = classcache_store(NULL,cls,false);
+ if (result == NULL)
+ return false;
+
+ if (result != cls) {
+ exceptions_throw_internalerror("class already stored in the class cache");
+ return false;
+ }
+
+ return true;
+}
+
+/* classcache_store_defined ****************************************************
+
+ Store a loaded class after it has been defined. If the class has already
+ been defined by the same defining loader in another thread, free the given
+ class and returned the one which has been defined earlier.
+
+ IN:
+ cls..............class object to store. classloader must be set
+ (classloader may be NULL, for bootloader)
+
+ RETURN VALUE:
+ cls..............everything ok, the class was stored the cache,
+ other classinfo..the class had already been defined, CLS was freed, the
+ class which was defined earlier is returned,
+ NULL.............an exception has been thrown.
+
+*******************************************************************************/
+
+classinfo *classcache_store_defined(classinfo *cls)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+#ifdef CLASSCACHE_VERBOSE
+ char logbuffer[1024];
+#endif
+
+ assert(cls);
+ assert(cls->state & CLASS_LOADED);
+
+ CLASSCACHE_LOCK();
+
+#ifdef CLASSCACHE_VERBOSE
+ sprintf(logbuffer,"classcache_store_defined (%p,", (void*)cls->classloader);
+ utf_cat_classname(logbuffer, cls->name);
+ strcat(logbuffer,")");
+ log_println(logbuffer);
+#endif
+
+ en = classcache_new_name(cls->name);
+
+ assert(en);
+
+ /* iterate over all class entries */
+ for (clsen = en->classes; clsen; clsen = clsen->next) {
+
+ /* check if this class has been defined by the same classloader */
+ if (clsen->classobj && clsen->classobj->classloader == cls->classloader) {
+ /* we found an earlier definition, delete the newer one */
+ /* (if it is a different classinfo) */
+ if (clsen->classobj != cls) {
+#ifdef CLASSCACHE_VERBOSE
+ log_println("replacing %p with earlier defined class %p",cls,clsen->classobj);
+#endif
+ class_free(cls);
+ cls = clsen->classobj;
+ }
+ goto return_success;
+ }
+ }
+
+ /* create a new class entry for this class object */
+ /* the list of initiating loaders is empty at this point */
+
+ clsen = NEW(classcache_class_entry);
+ clsen->classobj = cls;
+ clsen->loaders = NULL;
+ clsen->constraints = NULL;
+
+ clsen->next = en->classes;
+ en->classes = clsen;
+ CLASSCACHE_COUNT(stat_classes_stored);
+
+return_success:
+#ifdef CLASSCACHE_VERBOSE
+ classcache_debug_dump(stdout,cls->name);
+#endif
+ CLASSCACHE_UNLOCK();
+ return cls;
+}
+
+/* classcache_find_loader ******************************************************
+
+ Find the class entry loaded by or constrained to a given loader
+ (internally used helper function)
+
+ IN:
+ entry............the classcache_name_entry
+ loader...........the loader to look for
+
+ RETURN VALUE:
+ the classcache_class_entry for the given loader, or
+ NULL if no entry was found
+
+*******************************************************************************/
+
+static classcache_class_entry * classcache_find_loader(
+ classcache_name_entry * entry,
+ classloader_t * loader)
+{
+ classcache_class_entry *clsen;
+ classcache_loader_entry *lden;
+
+ assert(entry);
+
+ /* iterate over all class entries */
+ for (clsen = entry->classes; clsen; clsen = clsen->next) {
+
+ /* check if this entry has already been loaded by initloader */
+ for (lden = clsen->loaders; lden; lden = lden->next) {
+ if (lden->loader == loader)
+ return clsen; /* found */
+ }
+
+ /* check if loader is constrained to this entry */
+ for (lden = clsen->constraints; lden; lden = lden->next) {
+ if (lden->loader == loader)
+ return clsen; /* found */
+ }
+ }
+
+ /* not found */
+ return NULL;
+}
+
+/* classcache_free_class_entry *************************************************
+
+ Free the memory used by a class entry
+
+ IN:
+ clsen............the classcache_class_entry to free
+
+*******************************************************************************/
+
+static void classcache_free_class_entry(classcache_class_entry * clsen)
+{
+ classcache_loader_entry *lden;
+ classcache_loader_entry *next;
+
+ assert(clsen);
+
+ for (lden = clsen->loaders; lden; lden = next) {
+ next = lden->next;
+ FREE(lden, classcache_loader_entry);
+ }
+ for (lden = clsen->constraints; lden; lden = next) {
+ next = lden->next;
+ FREE(lden, classcache_loader_entry);
+ }
+
+ FREE(clsen, classcache_class_entry);
+}
+
+/* classcache_remove_class_entry ***********************************************
+
+ Remove a classcache_class_entry from the list of possible resolution of
+ a name entry
+ (internally used helper function)
+
+ IN:
+ entry............the classcache_name_entry
+ clsen............the classcache_class_entry to remove
+
+*******************************************************************************/
+
+static void classcache_remove_class_entry(classcache_name_entry * entry,
+ classcache_class_entry * clsen)
+{
+ classcache_class_entry **chain;
+
+ assert(entry);
+ assert(clsen);
+
+ chain = &(entry->classes);
+ while (*chain) {
+ if (*chain == clsen) {
+ *chain = clsen->next;
+ classcache_free_class_entry(clsen);
+ return;
+ }
+ chain = &((*chain)->next);
+ }
+}
+
+/* classcache_free_name_entry **************************************************
+
+ Free the memory used by a name entry
+
+ IN:
+ entry............the classcache_name_entry to free
+
+*******************************************************************************/
+
+static void classcache_free_name_entry(classcache_name_entry * entry)
+{
+ classcache_class_entry *clsen;
+ classcache_class_entry *next;
+
+ assert(entry);
+
+ for (clsen = entry->classes; clsen; clsen = next) {
+ next = clsen->next;
+ classcache_free_class_entry(clsen);
+ }
+
+ FREE(entry, classcache_name_entry);
+}
+
+/* classcache_free *************************************************************
+
+ Free the memory used by the class cache
+
+ NOTE:
+ The class cache may not be used any more after this call, except
+ when it is reinitialized with classcache_init.
+
+ Note: NOT synchronized!
+
+*******************************************************************************/
+
+void classcache_free(void)
+{
+ u4 slot;
+ classcache_name_entry *entry;
+ classcache_name_entry *next;
+
+ for (slot = 0; slot < hashtable_classcache.size; ++slot) {
+ for (entry = (classcache_name_entry *) hashtable_classcache.ptr[slot]; entry; entry = next) {
+ next = entry->hashlink;
+ classcache_free_name_entry(entry);
+ }
+ }
+
+ MFREE(hashtable_classcache.ptr, void*, hashtable_classcache.size);
+ hashtable_classcache.size = 0;
+ hashtable_classcache.entries = 0;
+ hashtable_classcache.ptr = NULL;
+}
+
+/* classcache_add_constraint ***************************************************
+
+ Add a loading constraint
+
+ IN:
+ a................first initiating loader
+ b................second initiating loader
+ classname........class name
+
+ RETURN VALUE:
+ true.............everything ok, the constraint has been added,
+ false............an exception has been thrown.
+
+ Note: synchronized with global tablelock
+
+*******************************************************************************/
+
+#if defined(ENABLE_VERIFIER)
+bool classcache_add_constraint(classloader_t * a,
+ classloader_t * b,
+ utf * classname)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsenA;
+ classcache_class_entry *clsenB;
+
+ assert(classname);
+
+#ifdef CLASSCACHE_VERBOSE
+ log_start();
+ log_print("classcache_add_constraint(%p,%p,", (void *) a, (void *) b);
+ utf_fprint_printable_ascii_classname(stdout, classname);
+ log_print(")\n");
+ log_finish();
+#endif
+
+ /* a constraint with a == b is trivially satisfied */
+ if (a == b) {
+ CLASSCACHE_COUNT(stat_trivial_constraints);
+ return true;
+ }
+
+ CLASSCACHE_LOCK();
+
+ en = classcache_new_name(classname);
+
+ assert(en);
+ CLASSCACHE_COUNT(stat_nontriv_constraints);
+
+ /* find the entry loaded by / constrained to each loader */
+ clsenA = classcache_find_loader(en, a);
+ clsenB = classcache_find_loader(en, b);
+
+ if (clsenA && clsenB) {
+ /* { both loaders have corresponding entries } */
+ CLASSCACHE_COUNT(stat_nontriv_constraints_both);
+
+ /* if the entries are the same, the constraint is already recorded */
+ if (clsenA == clsenB)
+ goto return_success;
+
+ /* check if the entries can be merged */
+ if (clsenA->classobj && clsenB->classobj
+ && clsenA->classobj != clsenB->classobj) {
+ /* no, the constraint is violated */
+ exceptions_throw_linkageerror("loading constraint violated: ",
+ clsenA->classobj);
+ goto return_exception;
+ }
+
+ /* yes, merge the entries */
+ classcache_merge_class_entries(en,clsenA,clsenB);
+ CLASSCACHE_COUNT(stat_nontriv_constraints_merged);
+ }
+ else {
+ /* { at most one of the loaders has a corresponding entry } */
+
+ /* set clsenA to the single class entry we have */
+ if (!clsenA)
+ clsenA = clsenB;
+
+ if (!clsenA) {
+ /* { no loader has a corresponding entry } */
+ CLASSCACHE_COUNT(stat_nontriv_constraints_none);
+
+ /* create a new class entry with the constraint (a,b,en->name) */
+ clsenA = NEW(classcache_class_entry);
+ clsenA->classobj = NULL;
+ clsenA->loaders = NULL;
+ clsenA->constraints = classcache_new_loader_entry(b, NULL);
+ clsenA->constraints = classcache_new_loader_entry(a, clsenA->constraints);
+
+ clsenA->next = en->classes;
+ en->classes = clsenA;
+ }
+ else {
+ CLASSCACHE_COUNT(stat_nontriv_constraints_one);
+
+ /* make b the loader that has no corresponding entry */
+ if (clsenB)
+ b = a;
+
+ /* loader b must be added to entry clsenA */
+ clsenA->constraints = classcache_new_loader_entry(b, clsenA->constraints);
+ }
+ }
+
+ return_success:
+ CLASSCACHE_UNLOCK();
+ return true;
+
+ return_exception:
+ CLASSCACHE_UNLOCK();
+ return false; /* exception */
+}
+#endif /* defined(ENABLE_VERIFIER) */
+
+/* classcache_add_constraints_for_params ***************************************
+
+ Add loading constraints for the parameters and return type of
+ the given method.
+
+ IN:
+ a................first initiating loader
+ b................second initiating loader
+ m................methodinfo
+
+ RETURN VALUE:
+ true.............everything ok, the constraints have been added,
+ false............an exception has been thrown.
+
+ Note: synchronized with global tablelock
+
+*******************************************************************************/
+
+#if defined(ENABLE_VERIFIER)
+bool classcache_add_constraints_for_params(classloader_t * a,
+ classloader_t * b,
+ methodinfo *m)
+{
+ methoddesc *md;
+ typedesc *td;
+ s4 i;
+
+ /* a constraint with a == b is trivially satisfied */
+
+ if (a == b) {
+ return true;
+ }
+
+ /* get the parsed descriptor */
+
+ assert(m);
+ md = m->parseddesc;
+ assert(md);
+
+ /* constrain the return type */
+
+ if (md->returntype.type == TYPE_ADR) {
+ if (!classcache_add_constraint(a, b, md->returntype.classref->name))
+ return false; /* exception */
+ }
+
+ /* constrain each reference type used in the parameters */
+
+ td = md->paramtypes;
+ i = md->paramcount;
+ for (; i--; td++) {
+ if (td->type != TYPE_ADR)
+ continue;
+
+ if (!classcache_add_constraint(a, b, td->classref->name))
+ return false; /* exception */
+ }
+
+ /* everything ok */
+ return true;
+}
+#endif /* defined(ENABLE_VERIFIER) */
+
+
+/* classcache_number_of_loaded_classes *****************************************
+
+ Counts the number of loaded classes and returns it.
+
+ Note: This function assumes that the CLASSCACHE_LOCK is held by the
+ caller!
+
+*******************************************************************************/
+
+static s4 classcache_number_of_loaded_classes(void)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+ s4 number;
+ s4 i;
+
+ /* initialize class counter */
+
+ number = 0;
+
+ for (i = 0; i < hashtable_classcache.size; i++) {
+ /* iterate over hashlink */
+
+ for (en = (classcache_name_entry*) hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
+ /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
+
+ if (en->name->text[0] == '$')
+ continue;
+
+ /* iterate over classes with same name */
+
+ for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+ /* get only loaded classes */
+
+ if (clsen->classobj != NULL)
+ number++;
+ }
+ }
+ }
+
+ return number;
+}
+
+
+/* classcache_get_loaded_class_count *******************************************
+
+ Counts the number of loaded classes and returns it.
+
+*******************************************************************************/
+
+s4 classcache_get_loaded_class_count(void)
+{
+ s4 count;
+
+ CLASSCACHE_LOCK();
+
+ count = classcache_number_of_loaded_classes();
+
+ CLASSCACHE_UNLOCK();
+
+ return count;
+}
+
+
+/* classcache_get_loaded_classes ***********************************************
+
+ Returns an array of all loaded classes as array. The array is
+ allocaed on the Java heap.
+
+*******************************************************************************/
+
+#if defined(ENABLE_JVMTI)
+void classcache_get_loaded_classes(s4 *class_count_ptr,
+ classinfo ***classes_ptr)
+{
+ classinfo **classes;
+ s4 class_count;
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+ s4 i;
+ s4 j;
+
+ CLASSCACHE_LOCK();
+
+ /* get the number of loaded classes and allocate the array */
+
+ class_count = classcache_number_of_loaded_classes();
+
+ classes = GCMNEW(classinfo*, class_count);
+
+ /* look in every slot of the hashtable */
+
+ for (i = 0, j = 0; i < hashtable_classcache.size; i++) {
+ /* iterate over hashlink */
+
+ for (en = hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
+ /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
+
+ if (en->name->text[0] == '$')
+ continue;
+
+ /* iterate over classes with same name */
+
+ for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+ /* get only loaded classes */
+
+ if (clsen->classobj != NULL) {
+ classes[j] = clsen->classobj;
+ j++;
+ }
+ }
+ }
+ }
+
+ /* pass the return values */
+
+ *class_count_ptr = class_count;
+ *classes_ptr = classes;
+
+ CLASSCACHE_UNLOCK();
+}
+#endif /* defined(ENABLE_JVMTI) */
+
+
+/* classcache_foreach_loaded_class *********************************************
+
+ Calls the given function for each loaded class.
+
+*******************************************************************************/
+
+void classcache_foreach_loaded_class(classcache_foreach_functionptr_t func,
+ void *data)
+{
+ classcache_name_entry *en;
+ classcache_class_entry *clsen;
+ s4 i;
+
+ CLASSCACHE_LOCK();
+
+ /* look in every slot of the hashtable */
+
+ for (i = 0; i < hashtable_classcache.size; i++) {
+ /* iterate over hashlink */
+
+ for (en = (classcache_name_entry*) hashtable_classcache.ptr[i]; en != NULL; en = en->hashlink) {
+ /* filter pseudo classes $NEW$, $NULL$, $ARRAYSTUB$ out */
+
+ if (en->name->text[0] == '$')
+ continue;
+
+ /* iterate over classes with same name */
+
+ for (clsen = en->classes; clsen != NULL; clsen = clsen->next) {
+ /* get only loaded classes */
+
+ if (clsen->classobj != NULL) {
+ (*func)(clsen->classobj, data);
+ }
+ }
+ }
+ }
+
+ CLASSCACHE_UNLOCK();
+}
+
+
+/*============================================================================*/
+/* DEBUG DUMPS */
+/*============================================================================*/
+
+/* classcache_debug_dump *******************************************************
+
+ Print the contents of the loaded class cache to a stream
+
+ IN:
+ file.............output stream
+ only.............if != NULL, only print entries for this name
+ (Currently we print also the rest of the hash chain to
+ get a feel for the average length of hash chains.)
+
+ Note: synchronized with global tablelock
+
+*******************************************************************************/
+
+#ifndef NDEBUG
+void classcache_debug_dump(FILE * file,utf *only)
+{
+ classcache_name_entry *c;
+ classcache_class_entry *clsen;
+ classcache_loader_entry *lden;
+ u4 slot;
+
+ CLASSCACHE_LOCK();
+
+ log_println("=== [loaded class cache] =====================================");
+ log_println("hash size : %d", (int) hashtable_classcache.size);
+ log_println("hash entries: %d", (int) hashtable_classcache.entries);
+ log_println("");
+
+ if (only) {
+ c = classcache_lookup_name(only);
+ slot = 0; /* avoid compiler warning */
+ goto dump_it;
+ }
+
+ for (slot = 0; slot < hashtable_classcache.size; ++slot) {
+ c = (classcache_name_entry *) hashtable_classcache.ptr[slot];
+
+dump_it:
+ for (; c; c = c->hashlink) {
+ utf_fprint_printable_ascii_classname(file, c->name);
+ fprintf(file, "\n");
+
+ /* iterate over all class entries */
+ for (clsen = c->classes; clsen; clsen = clsen->next) {
+ if (clsen->classobj) {
+ log_println(" loaded %p", (void *) clsen->classobj);
+ }
+ else {
+ log_println(" unresolved");
+ }
+
+ log_start();
+ log_print(" loaders: ");
+ for (lden = clsen->loaders; lden; lden = lden->next) {
+ log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
+ }
+ log_finish();
+
+ log_start();
+ log_print(" constraints: ");
+ for (lden = clsen->constraints; lden; lden = lden->next) {
+ log_print("<%p> %p ", (void *) lden, (void *) lden->loader);
+ }
+ log_finish();
+ }
+ }
+
+ if (only)
+ break;
+ }
+ fprintf(file, "\n==============================================================\n\n");
+
+ CLASSCACHE_UNLOCK();
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* NDEBUG */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/classcache.h - loaded class cache and loading constraints
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _CLASSCACHE_H
-#define _CLASSCACHE_H
-
-#include "config.h"
-
-#include "vm/types.h"
-
-#include <stdio.h> /* for FILE */
-
-#include "toolbox/hashtable.h"
-
-#include "vm/class.hpp"
-#include "vm/global.h"
-#include "vm/loader.hpp"
-#include "vm/references.h"
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* forward declarations *******************************************************/
-
-typedef struct classcache_name_entry classcache_name_entry;
-typedef struct classcache_class_entry classcache_class_entry;
-typedef struct classcache_loader_entry classcache_loader_entry;
-
-/* global variables ***********************************************************/
-
-extern hashtable hashtable_classcache;
-
-
-/* structs ********************************************************************/
-
-/*----------------------------------------------------------------------------*/
-/* The Loaded Class Cache */
-/* */
-/* The loaded class cache is implemented as a two-level data structure. */
-/* */
-/* The first level is a hash table indexed by class names. For each class */
-/* name in the cache there is a classcache_name_entry, which collects all */
-/* information about classes with this class name. */
-/* */
-/* Second level: For each classcache_name_entry there is a list of */
-/* classcache_class_entry:s representing the possible different resolutions */
-/* of the class name. */
-/* */
-/* A classcache_class_entry records the following: */
-/* */
-/* - the loaded class object, if this entry has been resolved, otherwise NULL */
-/* - the list of initiating loaders which have resolved the class name to */
-/* this class object */
-/* - the list of initiating loaders which are constrained to resolve this */
-/* class name to this class object in the future */
-/* */
-/* The classcache_class_entry:s approximate the equivalence classes created */
-/* by the loading constraints and the equivalence of loaded classes. */
-/* */
-/* When a loading constraint (loaderA,loaderB,NAME) is added, then the */
-/* classcache_class_entry:s for NAME containing loaderA and loaderB resp. */
-/* must be merged into one entry. If this is impossible, because the entries */
-/* have already been resolved to different class objects, then the constraint */
-/* is violated and an expception must be thrown. */
-/*----------------------------------------------------------------------------*/
-
-
-/* classcache_name_entry
- *
- * For each classname a classcache_name_entry struct is created.
- */
-
-struct classcache_name_entry
-{
- utf *name; /* class name */
- classcache_name_entry *hashlink; /* link for external chaining */
- classcache_class_entry *classes; /* equivalence classes for this name*/
-};
-
-struct classcache_class_entry
-{
- classinfo *classobj; /* the loaded class object, or NULL */
- classcache_loader_entry *loaders;
- classcache_loader_entry *constraints;
- classcache_class_entry *next; /* next class entry for same name */
-};
-
-struct classcache_loader_entry
-{
- classloader_t *loader; /* class loader object */
- classcache_loader_entry *next; /* next loader entry in the list */
-};
-
-
-/* callback function type for classcache_foreach_loaded_class */
-
-typedef void (*classcache_foreach_functionptr_t)(classinfo *, void *);
-
-
-/* function prototypes ********************************************************/
-
-/* initialize the loaded class cache */
-bool classcache_init(void);
-void classcache_free(void);
-
-classinfo * classcache_lookup(classloader_t *initloader,utf *classname);
-classinfo * classcache_lookup_defined(classloader_t *defloader,utf *classname);
-classinfo * classcache_lookup_defined_or_initiated(classloader_t *loader,utf *classname);
-
-bool classcache_store_unique(classinfo *cls);
-classinfo * classcache_store(classloader_t *initloader,classinfo *cls,bool mayfree);
-classinfo * classcache_store_defined(classinfo *cls);
-
-#if defined(ENABLE_VERIFIER)
-bool classcache_add_constraint(classloader_t *a,classloader_t *b,utf *classname);
-bool classcache_add_constraints_for_params(classloader_t *a,classloader_t *b,
- methodinfo *m);
-#endif
-
-s4 classcache_get_loaded_class_count(void);
-
-void classcache_foreach_loaded_class(classcache_foreach_functionptr_t func,
- void *data);
-
-#if defined(ENABLE_JVMTI)
-void classcache_get_loaded_classes(s4 *class_count_ptr,
- classinfo ***classes_ptr);
-#endif
-
-#ifndef NDEBUG
-void classcache_debug_dump(FILE *file,utf *only);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _CLASSCACHE_H */
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
-
--- /dev/null
+/* src/vm/classcache.hpp - loaded class cache and loading constraints
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _CLASSCACHE_H
+#define _CLASSCACHE_H
+
+#include "config.h"
+
+#include "vm/types.h"
+
+#include <stdio.h> /* for FILE */
+
+#include "toolbox/hashtable.h"
+
+#include "vm/class.hpp"
+#include "vm/global.h"
+#include "vm/loader.hpp"
+#include "vm/references.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* forward declarations *******************************************************/
+
+typedef struct classcache_name_entry classcache_name_entry;
+typedef struct classcache_class_entry classcache_class_entry;
+typedef struct classcache_loader_entry classcache_loader_entry;
+
+/* global variables ***********************************************************/
+
+extern hashtable hashtable_classcache;
+
+
+/* structs ********************************************************************/
+
+/*----------------------------------------------------------------------------*/
+/* The Loaded Class Cache */
+/* */
+/* The loaded class cache is implemented as a two-level data structure. */
+/* */
+/* The first level is a hash table indexed by class names. For each class */
+/* name in the cache there is a classcache_name_entry, which collects all */
+/* information about classes with this class name. */
+/* */
+/* Second level: For each classcache_name_entry there is a list of */
+/* classcache_class_entry:s representing the possible different resolutions */
+/* of the class name. */
+/* */
+/* A classcache_class_entry records the following: */
+/* */
+/* - the loaded class object, if this entry has been resolved, otherwise NULL */
+/* - the list of initiating loaders which have resolved the class name to */
+/* this class object */
+/* - the list of initiating loaders which are constrained to resolve this */
+/* class name to this class object in the future */
+/* */
+/* The classcache_class_entry:s approximate the equivalence classes created */
+/* by the loading constraints and the equivalence of loaded classes. */
+/* */
+/* When a loading constraint (loaderA,loaderB,NAME) is added, then the */
+/* classcache_class_entry:s for NAME containing loaderA and loaderB resp. */
+/* must be merged into one entry. If this is impossible, because the entries */
+/* have already been resolved to different class objects, then the constraint */
+/* is violated and an expception must be thrown. */
+/*----------------------------------------------------------------------------*/
+
+
+/* classcache_name_entry
+ *
+ * For each classname a classcache_name_entry struct is created.
+ */
+
+struct classcache_name_entry
+{
+ utf *name; /* class name */
+ classcache_name_entry *hashlink; /* link for external chaining */
+ classcache_class_entry *classes; /* equivalence classes for this name*/
+};
+
+struct classcache_class_entry
+{
+ classinfo *classobj; /* the loaded class object, or NULL */
+ classcache_loader_entry *loaders;
+ classcache_loader_entry *constraints;
+ classcache_class_entry *next; /* next class entry for same name */
+};
+
+struct classcache_loader_entry
+{
+ classloader_t *loader; /* class loader object */
+ classcache_loader_entry *next; /* next loader entry in the list */
+};
+
+
+/* callback function type for classcache_foreach_loaded_class */
+
+typedef void (*classcache_foreach_functionptr_t)(classinfo *, void *);
+
+
+/* function prototypes ********************************************************/
+
+/* initialize the loaded class cache */
+bool classcache_init(void);
+void classcache_free(void);
+
+classinfo * classcache_lookup(classloader_t *initloader,utf *classname);
+classinfo * classcache_lookup_defined(classloader_t *defloader,utf *classname);
+classinfo * classcache_lookup_defined_or_initiated(classloader_t *loader,utf *classname);
+
+bool classcache_store_unique(classinfo *cls);
+classinfo * classcache_store(classloader_t *initloader,classinfo *cls,bool mayfree);
+classinfo * classcache_store_defined(classinfo *cls);
+
+#if defined(ENABLE_VERIFIER)
+bool classcache_add_constraint(classloader_t *a,classloader_t *b,utf *classname);
+bool classcache_add_constraints_for_params(classloader_t *a,classloader_t *b,
+ methodinfo *m);
+#endif
+
+s4 classcache_get_loaded_class_count(void);
+
+void classcache_foreach_loaded_class(classcache_foreach_functionptr_t func,
+ void *data);
+
+#if defined(ENABLE_JVMTI)
+void classcache_get_loaded_classes(s4 *class_count_ptr,
+ classinfo ***classes_ptr);
+#endif
+
+#ifndef NDEBUG
+void classcache_debug_dump(FILE *file,utf *only);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CLASSCACHE_H */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+
+++ /dev/null
-/* src/vm/descriptor.c - checking and parsing of field / method descriptors
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-
-#include "vm/types.h"
-
-#include "md-abi.h"
-
-#include "mm/memory.h"
-
-#include "vm/descriptor.h"
-#include "vm/exceptions.hpp"
-#include "vm/options.h"
-#include "vm/primitive.hpp"
-#include "vm/vm.hpp"
-
-#include "vm/jit/abi.h"
-
-
-/* constants (private to descriptor.c) ****************************************/
-
-/* initial number of entries for the classrefhash of a descriptor_pool */
-/* (currently the hash is never grown!) */
-#define CLASSREFHASH_INIT_SIZE 64
-
-/* initial number of entries for the descriptorhash of a descriptor_pool */
-/* (currently the hash is never grown!) */
-#define DESCRIPTORHASH_INIT_SIZE 128
-
-/* data structures (private to descriptor.c) **********************************/
-
-typedef struct classref_hash_entry classref_hash_entry;
-typedef struct descriptor_hash_entry descriptor_hash_entry;
-
-/* entry struct for the classrefhash of descriptor_pool */
-struct classref_hash_entry {
- classref_hash_entry *hashlink; /* for hash chaining */
- utf *name; /* name of the class refered to */
- u2 index; /* index into classref table */
-};
-
-/* entry struct for the descriptorhash of descriptor_pool */
-struct descriptor_hash_entry {
- descriptor_hash_entry *hashlink;
- utf *desc;
- parseddesc_t parseddesc;
- s2 paramslots; /* number of params, LONG/DOUBLE counted as 2 */
-};
-
-
-/****************************************************************************/
-/* MACROS FOR DESCRIPTOR PARSING (private to descriptor.c) */
-/****************************************************************************/
-
-/* SKIP_FIELDDESCRIPTOR:
- * utf_ptr must point to the first character of a field descriptor.
- * After the macro call utf_ptr points to the first character after
- * the field descriptor.
- *
- * CAUTION: This macro does not check for an unexpected end of the
- * descriptor. Better use SKIP_FIELDDESCRIPTOR_SAFE.
- */
-#define SKIP_FIELDDESCRIPTOR(utf_ptr) \
- do { while (*(utf_ptr)=='[') (utf_ptr)++; \
- if (*(utf_ptr)++=='L') \
- while(*(utf_ptr)++ != ';') /* skip */; } while(0)
-
-/* SKIP_FIELDDESCRIPTOR_SAFE:
- * utf_ptr must point to the first character of a field descriptor.
- * After the macro call utf_ptr points to the first character after
- * the field descriptor.
- *
- * Input:
- * utf_ptr....points to first char of descriptor
- * end_ptr....points to first char after the end of the string
- * errorflag..must be initialized (to false) by the caller!
- * Output:
- * utf_ptr....points to first char after the descriptor
- * errorflag..set to true if the string ended unexpectedly
- */
-#define SKIP_FIELDDESCRIPTOR_SAFE(utf_ptr,end_ptr,errorflag) \
- do { while ((utf_ptr) != (end_ptr) && *(utf_ptr)=='[') (utf_ptr)++; \
- if ((utf_ptr) == (end_ptr)) \
- (errorflag) = true; \
- else \
- if (*(utf_ptr)++=='L') { \
- while((utf_ptr) != (end_ptr) && *(utf_ptr)++ != ';') \
- /* skip */; \
- if ((utf_ptr)[-1] != ';') \
- (errorflag) = true; }} while(0)
-
-
-/****************************************************************************/
-/* DEBUG HELPERS */
-/****************************************************************************/
-
-/*#define DESCRIPTOR_VERBOSE*/
-
-/****************************************************************************/
-/* FUNCTIONS */
-/****************************************************************************/
-
-/* descriptor_to_basic_type ****************************************************
-
- Return the basic type to use for a value with this descriptor.
-
- IN:
- utf..............descriptor utf string
-
- OUT:
- A TYPE_* constant.
-
- PRECONDITIONS:
- This function assumes that the descriptor has passed
- descriptor_pool_add checks and that it does not start with '('.
-
-*******************************************************************************/
-
-int descriptor_to_basic_type(utf *descriptor)
-{
- assert(descriptor->blength >= 1);
-
- switch (descriptor->text[0]) {
- case 'Z':
- case 'B':
- case 'C':
- case 'S':
- case 'I':
- return TYPE_INT;
-
- case 'J':
- return TYPE_LNG;
-
- case 'F':
- return TYPE_FLT;
-
- case 'D':
- return TYPE_DBL;
-
- case 'L':
- case '[':
- return TYPE_ADR;
-
- default:
- vm_abort("descriptor_to_basic_type: invalid type %c",
- descriptor->text[0]);
- }
-
- /* keep the compiler happy */
-
- return 0;
-}
-
-
-/* descriptor_typesize *********************************************************
-
- Return the size in bytes needed for the given type.
-
- IN:
- td..............typedesc describing the type
-
- OUT:
- The number of bytes
-
-*******************************************************************************/
-
-int descriptor_typesize(typedesc *td)
-{
- assert(td);
-
- switch (td->type) {
- case TYPE_INT:
- case TYPE_FLT:
- return 4;
-
- case TYPE_LNG:
- case TYPE_DBL:
- return 8;
-
- case TYPE_ADR:
- return SIZEOF_VOID_P;
-
- default:
- vm_abort("descriptor_typesize: invalid type %d", td->type);
- }
-
- /* keep the compiler happy */
-
- return 0;
-}
-
-
-/* name_from_descriptor ********************************************************
-
- Return the class name indicated by the given descriptor
- (Internally used helper function)
-
- IN:
- c................class containing the descriptor
- utf_ptr..........first character of descriptor
- end_ptr..........first character after the end of the string
- mode.............a combination (binary or) of the following flags:
-
- (Flags marked with * are the default settings.)
-
- How to handle "V" descriptors:
-
- * DESCRIPTOR_VOID.....handle it like other primitive types
- DESCRIPTOR_NOVOID...treat it as an error
-
- How to deal with extra characters after the end of the
- descriptor:
-
- * DESCRIPTOR_NOCHECKEND...ignore (useful for parameter lists)
- DESCRIPTOR_CHECKEND.....treat them as an error
-
- OUT:
- *next............if non-NULL, *next is set to the first character after
- the descriptor. (Undefined if an error occurs.)
- *name............set to the utf name of the class
-
- RETURN VALUE:
- true.............descriptor parsed successfully
- false............an exception has been thrown
-
-*******************************************************************************/
-
-#define DESCRIPTOR_VOID 0 /* default */
-#define DESCRIPTOR_NOVOID 0x0040
-#define DESCRIPTOR_NOCHECKEND 0 /* default */
-#define DESCRIPTOR_CHECKEND 0x1000
-
-static bool
-name_from_descriptor(classinfo *c,
- char *utf_ptr, char *end_ptr,
- char **next, int mode, utf **name)
-{
- char *start = utf_ptr;
- bool error = false;
-
- assert(c);
- assert(utf_ptr);
- assert(end_ptr);
- assert(name);
-
- *name = NULL;
- SKIP_FIELDDESCRIPTOR_SAFE(utf_ptr, end_ptr, error);
-
- if (mode & DESCRIPTOR_CHECKEND)
- error |= (utf_ptr != end_ptr);
-
- if (!error) {
- if (next) *next = utf_ptr;
-
- switch (*start) {
- case 'V':
- if (mode & DESCRIPTOR_NOVOID)
- break;
- /* FALLTHROUGH! */
- case 'I':
- case 'J':
- case 'F':
- case 'D':
- case 'B':
- case 'C':
- case 'S':
- case 'Z':
- return true;
-
- case 'L':
- start++;
- utf_ptr--;
- /* FALLTHROUGH! */
- case '[':
- *name = utf_new(start, utf_ptr - start);
- return true;
- }
- }
-
- exceptions_throw_classformaterror(c, "Invalid descriptor");
- return false;
-}
-
-
-/* descriptor_to_typedesc ******************************************************
-
- Parse the given type descriptor and fill a typedesc struct
- (Internally used helper function)
-
- IN:
- pool.............the descriptor pool
- utf_ptr..........points to first character of type descriptor
- end_pos..........points after last character of the whole descriptor
-
- OUT:
- *next............set to next character after type descriptor
- *d...............filled with parsed information
-
- RETURN VALUE:
- true.............parsing succeeded
- false............an exception has been thrown
-
-*******************************************************************************/
-
-static bool
-descriptor_to_typedesc(descriptor_pool *pool, char *utf_ptr, char *end_pos,
- char **next, typedesc *td)
-{
- utf *name;
-
- if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, next, 0, &name))
- return false;
-
- if (name) {
- /* a reference type */
- td->type = TYPE_ADR;
- td->primitivetype = TYPE_ADR;
- td->arraydim = 0;
- for (utf_ptr = name->text; *utf_ptr == '['; ++utf_ptr)
- td->arraydim++;
- td->classref = descriptor_pool_lookup_classref(pool, name);
-
- } else {
- /* a primitive type */
- switch (*utf_ptr) {
- case 'B':
- td->primitivetype = PRIMITIVETYPE_BYTE;
- td->type = TYPE_INT;
- break;
- case 'C':
- td->primitivetype = PRIMITIVETYPE_CHAR;
- td->type = TYPE_INT;
- break;
- case 'S':
- td->primitivetype = PRIMITIVETYPE_SHORT;
- td->type = TYPE_INT;
- break;
- case 'Z':
- td->primitivetype = PRIMITIVETYPE_BOOLEAN;
- td->type = TYPE_INT;
- break;
- case 'I':
- td->primitivetype = PRIMITIVETYPE_INT;
- td->type = TYPE_INT;
- break;
- case 'D':
- td->primitivetype = PRIMITIVETYPE_DOUBLE;
- td->type = TYPE_DBL;
- break;
- case 'F':
- td->primitivetype = PRIMITIVETYPE_FLOAT;
- td->type = TYPE_FLT;
- break;
- case 'J':
- td->primitivetype = PRIMITIVETYPE_LONG;
- td->type = TYPE_LNG;
- break;
- case 'V':
- td->primitivetype = PRIMITIVETYPE_VOID;
- td->type = TYPE_VOID;
- break;
- default:
- assert(false);
- }
-
- td->arraydim = 0;
- td->classref = NULL;
- }
-
- return true;
-}
-
-
-/* descriptor_pool_new *********************************************************
-
- Allocate a new descriptor_pool
-
- IN:
- referer..........class for which to create the pool
-
- RETURN VALUE:
- a pointer to the new descriptor_pool
-
-*******************************************************************************/
-
-descriptor_pool *
-descriptor_pool_new(classinfo *referer)
-{
- descriptor_pool *pool;
- u4 hashsize;
- u4 slot;
-
- pool = DNEW(descriptor_pool);
- assert(pool);
-
- pool->referer = referer;
- pool->fieldcount = 0;
- pool->methodcount = 0;
- pool->paramcount = 0;
- pool->descriptorsize = 0;
- pool->descriptors = NULL;
- pool->descriptors_next = NULL;
- pool->classrefs = NULL;
- pool->descriptor_kind = NULL;
- pool->descriptor_kind_next = NULL;
-
- hashsize = CLASSREFHASH_INIT_SIZE;
- pool->classrefhash.size = hashsize;
- pool->classrefhash.entries = 0;
- pool->classrefhash.ptr = DMNEW(void*, hashsize);
- for (slot=0; slot<hashsize; ++slot)
- pool->classrefhash.ptr[slot] = NULL;
-
- hashsize = DESCRIPTORHASH_INIT_SIZE;
- pool->descriptorhash.size = hashsize;
- pool->descriptorhash.entries = 0;
- pool->descriptorhash.ptr = DMNEW(void*, hashsize);
- for (slot=0; slot<hashsize; ++slot)
- pool->descriptorhash.ptr[slot] = NULL;
-
- return pool;
-}
-
-
-/* descriptor_pool_add_class ***************************************************
-
- Add the given class reference to the pool
-
- IN:
- pool.............the descriptor_pool
- name.............the class reference to add
-
- RETURN VALUE:
- true.............reference has been added
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool
-descriptor_pool_add_class(descriptor_pool *pool, utf *name)
-{
- u4 key,slot;
- classref_hash_entry *c;
-
- assert(pool);
- assert(name);
-
-#ifdef DESCRIPTOR_VERBOSE
- fprintf(stderr,"descriptor_pool_add_class(%p,",(void*)pool);
- utf_fprint_printable_ascii(stderr,name);fprintf(stderr,")\n");
-#endif
-
- /* find a place in the hashtable */
-
- key = utf_hashkey(name->text, name->blength);
- slot = key & (pool->classrefhash.size - 1);
- c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
-
- while (c) {
- if (c->name == name)
- return true; /* already stored */
- c = c->hashlink;
- }
-
- /* check if the name is a valid classname */
-
- if (!is_valid_name(name->text,UTF_END(name))) {
- exceptions_throw_classformaterror(pool->referer, "Invalid class name");
- return false; /* exception */
- }
-
- /* XXX check maximum array dimension */
-
- c = DNEW(classref_hash_entry);
- c->name = name;
- c->index = pool->classrefhash.entries++;
- c->hashlink = (classref_hash_entry *) pool->classrefhash.ptr[slot];
- pool->classrefhash.ptr[slot] = c;
-
- return true;
-}
-
-
-/* descriptor_pool_add *********************************************************
-
- Check the given descriptor and add it to the pool
-
- IN:
- pool.............the descriptor_pool
- desc.............the descriptor to add. Maybe a field or method desc.
-
- OUT:
- *paramslots......if non-NULL, set to the number of parameters.
- LONG and DOUBLE are counted twice
-
- RETURN VALUE:
- true.............descriptor has been added
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool
-descriptor_pool_add(descriptor_pool *pool, utf *desc, int *paramslots)
-{
- u4 key,slot;
- descriptor_hash_entry *d;
- char *utf_ptr;
- char *end_pos;
- utf *name;
- s4 argcount = 0;
-
-#ifdef DESCRIPTOR_VERBOSE
- fprintf(stderr,"descriptor_pool_add(%p,",(void*)pool);
- utf_fprint_printable_ascii(stderr,desc);fprintf(stderr,")\n");
-#endif
-
- assert(pool);
- assert(desc);
-
- /* find a place in the hashtable */
-
- key = utf_hashkey(desc->text, desc->blength);
- slot = key & (pool->descriptorhash.size - 1);
- d = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
-
- /* Save all method descriptors in the hashtable, since the parsed */
- /* descriptor may vary between differenf methods (static vs. non-static). */
-
- utf_ptr = desc->text;
-
- if (*utf_ptr != '(') {
- while (d) {
- if (d->desc == desc) {
- if (paramslots)
- *paramslots = d->paramslots;
- return true; /* already stored */
- }
- d = d->hashlink;
- }
- }
-
- /* add the descriptor to the pool */
-
- d = DNEW(descriptor_hash_entry);
- d->desc = desc;
- d->parseddesc.any = NULL;
- d->hashlink = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
- pool->descriptorhash.ptr[slot] = d;
-
- /* now check the descriptor */
-
- end_pos = UTF_END(desc);
-
- if (*utf_ptr == '(') {
- /* a method descriptor */
-
- pool->methodcount++;
- utf_ptr++;
-
- /* check arguments */
-
- while ((utf_ptr != end_pos) && (*utf_ptr != ')')) {
- pool->paramcount++;
-
- /* We cannot count the `this' argument here because
- * we don't know if the method is static. */
-
- if (*utf_ptr == 'J' || *utf_ptr == 'D')
- argcount += 2;
- else
- argcount++;
-
- if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, &utf_ptr,
- DESCRIPTOR_NOVOID, &name))
- return false;
-
- if (name)
- if (!descriptor_pool_add_class(pool, name))
- return false;
- }
-
- if (utf_ptr == end_pos) {
- exceptions_throw_classformaterror(pool->referer,
- "Missing ')' in method descriptor");
- return false;
- }
-
- utf_ptr++; /* skip ')' */
-
- if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, NULL,
- DESCRIPTOR_CHECKEND, &name))
- return false;
-
- if (name)
- if (!descriptor_pool_add_class(pool,name))
- return false;
-
- if (argcount > 255) {
- exceptions_throw_classformaterror(pool->referer,
- "Too many arguments in signature");
- return false;
- }
-
- } else {
- /* a field descriptor */
-
- pool->fieldcount++;
-
- if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, NULL,
- DESCRIPTOR_NOVOID | DESCRIPTOR_CHECKEND,
- &name))
- return false;
-
- if (name)
- if (!descriptor_pool_add_class(pool,name))
- return false;
- }
-
- d->paramslots = argcount;
-
- if (paramslots)
- *paramslots = argcount;
-
- return true;
-}
-
-
-/* descriptor_pool_create_classrefs ********************************************
-
- Create a table containing all the classrefs which were added to the pool
-
- IN:
- pool.............the descriptor_pool
-
- OUT:
- *count...........if count is non-NULL, this is set to the number
- of classrefs in the table
-
- RETURN VALUE:
- a pointer to the constant_classref table
-
-*******************************************************************************/
-
-constant_classref *
-descriptor_pool_create_classrefs(descriptor_pool *pool, s4 *count)
-{
- u4 nclasses;
- u4 slot;
- classref_hash_entry *c;
- constant_classref *ref;
-
- assert(pool);
-
- nclasses = pool->classrefhash.entries;
- pool->classrefs = MNEW(constant_classref,nclasses);
-
- /* fill the constant_classref structs */
-
- for (slot = 0; slot < pool->classrefhash.size; ++slot) {
- c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
- while (c) {
- ref = pool->classrefs + c->index;
- CLASSREF_INIT(*ref, pool->referer, c->name);
- c = c->hashlink;
- }
- }
-
- if (count)
- *count = nclasses;
-
- return pool->classrefs;
-}
-
-
-/* descriptor_pool_lookup_classref *********************************************
-
- Return the constant_classref for the given class name
-
- IN:
- pool.............the descriptor_pool
- classname........name of the class to look up
-
- RETURN VALUE:
- a pointer to the constant_classref, or
- NULL if an exception has been thrown
-
-*******************************************************************************/
-
-constant_classref *
-descriptor_pool_lookup_classref(descriptor_pool *pool, utf *classname)
-{
- u4 key,slot;
- classref_hash_entry *c;
-
- assert(pool);
- assert(pool->classrefs);
- assert(classname);
-
- key = utf_hashkey(classname->text, classname->blength);
- slot = key & (pool->classrefhash.size - 1);
- c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
-
- while (c) {
- if (c->name == classname)
- return pool->classrefs + c->index;
- c = c->hashlink;
- }
-
- exceptions_throw_internalerror("Class reference not found in descriptor pool");
- return NULL;
-}
-
-
-/* descriptor_pool_alloc_parsed_descriptors ************************************
-
- Allocate space for the parsed descriptors
-
- IN:
- pool.............the descriptor_pool
-
- NOTE:
- This function must be called after all descriptors have been added
- with descriptor_pool_add.
-
-*******************************************************************************/
-
-void
-descriptor_pool_alloc_parsed_descriptors(descriptor_pool *pool)
-{
- u4 size;
-
- assert(pool);
-
- /* TWISTI: paramcount + 1: we don't know if the method is static or */
- /* not, i have no better solution yet. */
-
- size =
- pool->fieldcount * sizeof(typedesc) +
- pool->methodcount * (sizeof(methoddesc) - sizeof(typedesc)) +
- pool->paramcount * sizeof(typedesc) +
- pool->methodcount * sizeof(typedesc); /* possible `this' pointer */
-
- pool->descriptorsize = size;
- if (size) {
- pool->descriptors = MNEW(u1, size);
- pool->descriptors_next = pool->descriptors;
- }
-
- size = pool->fieldcount + pool->methodcount;
- if (size) {
- pool->descriptor_kind = DMNEW(u1, size);
- pool->descriptor_kind_next = pool->descriptor_kind;
- }
-}
-
-
-/* descriptor_pool_parse_field_descriptor **************************************
-
- Parse the given field descriptor
-
- IN:
- pool.............the descriptor_pool
- desc.............the field descriptor
-
- RETURN VALUE:
- a pointer to the parsed field descriptor, or
- NULL if an exception has been thrown
-
- NOTE:
- descriptor_pool_alloc_parsed_descriptors must be called (once)
- before this function is used.
-
-*******************************************************************************/
-
-typedesc *
-descriptor_pool_parse_field_descriptor(descriptor_pool *pool, utf *desc)
-{
- u4 key,slot;
- descriptor_hash_entry *d;
- typedesc *td;
-
- assert(pool);
- assert(pool->descriptors);
- assert(pool->descriptors_next);
-
- /* lookup the descriptor in the hashtable */
-
- key = utf_hashkey(desc->text, desc->blength);
- slot = key & (pool->descriptorhash.size - 1);
- d = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
-
- while (d) {
- if (d->desc == desc) {
- /* found */
- if (d->parseddesc.fd)
- return d->parseddesc.fd;
- break;
- }
- d = d->hashlink;
- }
-
- assert(d);
-
- if (desc->text[0] == '(') {
- exceptions_throw_classformaterror(pool->referer,
- "Method descriptor used in field reference");
- return NULL;
- }
-
- td = (typedesc *) pool->descriptors_next;
- pool->descriptors_next += sizeof(typedesc);
-
- if (!descriptor_to_typedesc(pool, desc->text, UTF_END(desc), NULL, td))
- return NULL;
-
- *(pool->descriptor_kind_next++) = 'f';
-
- d->parseddesc.fd = td;
-
- return td;
-}
-
-
-/* descriptor_pool_parse_method_descriptor *************************************
-
- Parse the given method descriptor
-
- IN:
- pool.............the descriptor_pool
- desc.............the method descriptor
- mflags...........the method flags
- thisclass........classref to the class containing the method.
- This is ignored if mflags contains ACC_STATIC.
- The classref is stored for inserting the 'this' argument.
-
- RETURN VALUE:
- a pointer to the parsed method descriptor, or
- NULL if an exception has been thrown
-
- NOTE:
- descriptor_pool_alloc_parsed_descriptors must be called
- (once) before this function is used.
-
-*******************************************************************************/
-
-methoddesc *
-descriptor_pool_parse_method_descriptor(descriptor_pool *pool, utf *desc,
- s4 mflags,constant_classref *thisclass)
-{
- u4 key, slot;
- descriptor_hash_entry *d;
- methoddesc *md;
- typedesc *td;
- char *utf_ptr;
- char *end_pos;
- s2 paramcount = 0;
- s2 paramslots = 0;
-
-#ifdef DESCRIPTOR_VERBOSE
- fprintf(stderr,"descriptor_pool_parse_method_descriptor(%p,%d,%p,",
- (void*)pool,(int)mflags,(void*)thisclass);
- utf_fprint_printable_ascii(stderr,desc); fprintf(stderr,")\n");
-#endif
-
- assert(pool);
- assert(pool->descriptors);
- assert(pool->descriptors_next);
-
- /* check that it is a method descriptor */
-
- if (desc->text[0] != '(') {
- exceptions_throw_classformaterror(pool->referer,
- "Field descriptor used in method reference");
- return NULL;
- }
-
- /* lookup the descriptor in the hashtable */
-
- key = utf_hashkey(desc->text, desc->blength);
- slot = key & (pool->descriptorhash.size - 1);
- d = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
-
- /* find an un-parsed descriptor */
-
- while (d) {
- if (d->desc == desc)
- if (!d->parseddesc.md)
- break;
- d = d->hashlink;
- }
-
- assert(d);
-
- md = (methoddesc *) pool->descriptors_next;
- pool->descriptors_next += sizeof(methoddesc) - sizeof(typedesc);
-
- utf_ptr = desc->text + 1; /* skip '(' */
- end_pos = UTF_END(desc);
-
- td = md->paramtypes;
-
- /* count the `this' pointer */
-
- if ((mflags != ACC_UNDEF) && !(mflags & ACC_STATIC)) {
- td->type = TYPE_ADR;
- td->primitivetype = TYPE_ADR;
- td->arraydim = 0;
- td->classref = thisclass;
-
- td++;
- pool->descriptors_next += sizeof(typedesc);
- paramcount++;
- paramslots++;
- }
-
- while (*utf_ptr != ')') {
- /* parse a parameter type */
-
- if (!descriptor_to_typedesc(pool, utf_ptr, end_pos, &utf_ptr, td))
- return NULL;
-
- if (IS_2_WORD_TYPE(td->type))
- paramslots++;
-
- td++;
- pool->descriptors_next += sizeof(typedesc);
- paramcount++;
- paramslots++;
- }
- utf_ptr++; /* skip ')' */
-
- /* Skip possible `this' pointer in paramtypes array to allow a possible */
- /* memory move later in parse. */
- /* We store the thisclass reference, so we can later correctly fill in */
- /* the parameter slot of the 'this' argument. */
-
- if (mflags == ACC_UNDEF) {
- td->classref = thisclass;
- td++;
- pool->descriptors_next += sizeof(typedesc);
- }
-
- /* parse return type */
-
- if (!descriptor_to_typedesc(pool, utf_ptr, end_pos, NULL,
- &(md->returntype)))
- return NULL;
-
- md->paramcount = paramcount;
- md->paramslots = paramslots;
-
- /* If mflags != ACC_UNDEF we parse a real loaded method, so do
- param prealloc. Otherwise we do this in stack analysis. */
-
- if (mflags != ACC_UNDEF) {
- if (md->paramcount > 0) {
- /* allocate memory for params */
-
- md->params = MNEW(paramdesc, md->paramcount);
- }
- else {
- md->params = METHODDESC_NOPARAMS;
- }
-
- /* fill the paramdesc */
- /* md_param_alloc has to be called if md->paramcount == 0,
- too, so it can make the reservation for the Linkage Area,
- Return Register... */
-
-#if defined(ENABLE_JIT)
-# if defined(ENABLE_INTRP)
- if (!opt_intrp)
-# endif
- {
- /* As builtin-functions are native functions, we have
- to pre-allocate for the native ABI. */
-
- if (mflags & ACC_METHOD_BUILTIN)
- md_param_alloc_native(md);
- else
- md_param_alloc(md);
- }
-#endif
- }
- else {
- /* params will be allocated later by
- descriptor_params_from_paramtypes if necessary */
-
- md->params = NULL;
- }
-
- *(pool->descriptor_kind_next++) = 'm';
-
- d->parseddesc.md = md;
-
- return md;
-}
-
-/* descriptor_params_from_paramtypes *******************************************
-
- Create the paramdescs for a method descriptor. This function is called
- when we know whether the method is static or not. This function may only
- be called once for each methoddesc, and only if md->params == NULL.
-
- IN:
- md...............the parsed method descriptor
- md->params MUST be NULL.
- mflags...........the ACC_* access flags of the method. Only the
- ACC_STATIC bit is checked.
- The value ACC_UNDEF is NOT allowed.
-
- RETURN VALUE:
- true.............the paramdescs were created successfully
- false............an exception has been thrown
-
- POSTCONDITION:
- md->parms != NULL
-
-*******************************************************************************/
-
-bool descriptor_params_from_paramtypes(methoddesc *md, s4 mflags)
-{
- typedesc *td;
-
- assert(md);
- assert(md->params == NULL);
- assert(mflags != ACC_UNDEF);
-
- td = md->paramtypes;
-
- /* check for `this' pointer */
-
- if (!(mflags & ACC_STATIC)) {
- constant_classref *thisclass;
-
- /* fetch class reference from reserved param slot */
- thisclass = td[md->paramcount].classref;
- assert(thisclass);
-
- if (md->paramcount > 0) {
- /* shift param types by 1 argument */
- MMOVE(td + 1, td, typedesc, md->paramcount);
- }
-
- /* fill in first argument `this' */
-
- td->type = TYPE_ADR;
- td->primitivetype = TYPE_ADR;
- td->arraydim = 0;
- td->classref = thisclass;
-
- md->paramcount++;
- md->paramslots++;
- }
-
- /* if the method has params, process them */
-
- if (md->paramcount > 0) {
- /* allocate memory for params */
-
- md->params = MNEW(paramdesc, md->paramcount);
-
- } else {
- md->params = METHODDESC_NOPARAMS;
- }
-
- /* fill the paramdesc */
- /* md_param_alloc has to be called if md->paramcount == 0, too, so
- it can make the reservation for the Linkage Area, Return
- Register.. */
-
-#if defined(ENABLE_JIT)
-# if defined(ENABLE_INTRP)
- if (!opt_intrp)
-# endif
- {
- /* As builtin-functions are native functions, we have to
- pre-allocate for the native ABI. */
-
- if (mflags & ACC_METHOD_BUILTIN)
- md_param_alloc_native(md);
- else
- md_param_alloc(md);
- }
-#endif
-
- return true;
-}
-
-
-/* descriptor_pool_get_parsed_descriptors **************************************
-
- Return a pointer to the block of parsed descriptors
-
- IN:
- pool.............the descriptor_pool
-
- OUT:
- *size............if size is non-NULL, this is set to the size of the
- parsed descriptor block (in u1)
-
- RETURN VALUE:
- a pointer to the block of parsed descriptors
-
- NOTE:
- descriptor_pool_alloc_parsed_descriptors must be called (once)
- before this function is used.
-
-*******************************************************************************/
-
-void *
-descriptor_pool_get_parsed_descriptors(descriptor_pool *pool, s4 *size)
-{
- assert(pool);
- assert((!pool->fieldcount && !pool->methodcount) || pool->descriptors);
-
- if (size)
- *size = pool->descriptorsize;
-
- return pool->descriptors;
-}
-
-
-/* descriptor_pool_get_sizes ***************************************************
-
- Get the sizes of the class reference table and the parsed descriptors
-
- IN:
- pool.............the descriptor_pool
-
- OUT:
- *classrefsize....set to size of the class reference table
- *descsize........set to size of the parsed descriptors
-
- NOTE:
- This function may only be called after both
- descriptor_pool_create_classrefs, and
- descriptor_pool_alloc_parsed_descriptors
- have been called.
-
-*******************************************************************************/
-
-void
-descriptor_pool_get_sizes(descriptor_pool *pool, u4 *classrefsize, u4 *descsize)
-{
- assert(pool);
- assert((!pool->fieldcount && !pool->methodcount) || pool->descriptors);
- assert(pool->classrefs);
- assert(classrefsize);
- assert(descsize);
-
- *classrefsize = pool->classrefhash.entries * sizeof(constant_classref);
- *descsize = pool->descriptorsize;
-}
-
-
-/****************************************************************************/
-/* DEBUG HELPERS */
-/****************************************************************************/
-
-#ifndef NDEBUG
-/* descriptor_debug_print_typedesc *********************************************
-
- Print the given typedesc to the given stream
-
- IN:
- file.............stream to print to
- d................the parsed descriptor
-
-*******************************************************************************/
-
-void
-descriptor_debug_print_typedesc(FILE *file,typedesc *d)
-{
- int ch;
-
- if (!d) {
- fprintf(file,"(typedesc *)NULL");
- return;
- }
-
- if (d->type == TYPE_ADR) {
- if (d->classref)
- utf_fprint_printable_ascii(file,d->classref->name);
- else
- fprintf(file,"<class=NULL>");
- }
- else {
- switch (d->primitivetype) {
- case PRIMITIVETYPE_INT : ch='I'; break;
- case PRIMITIVETYPE_CHAR : ch='C'; break;
- case PRIMITIVETYPE_BYTE : ch='B'; break;
- case PRIMITIVETYPE_SHORT : ch='S'; break;
- case PRIMITIVETYPE_BOOLEAN: ch='Z'; break;
- case PRIMITIVETYPE_LONG : ch='J'; break;
- case PRIMITIVETYPE_FLOAT : ch='F'; break;
- case PRIMITIVETYPE_DOUBLE : ch='D'; break;
- case PRIMITIVETYPE_VOID : ch='V'; break;
- default : ch='!';
- }
- fputc(ch,file);
- }
- if (d->arraydim)
- fprintf(file,"[%d]",d->arraydim);
-}
-
-/* descriptor_debug_print_paramdesc ********************************************
-
- Print the given paramdesc to the given stream
-
- IN:
- file.............stream to print to
- d................the parameter descriptor
-
-*******************************************************************************/
-
-void
-descriptor_debug_print_paramdesc(FILE *file,paramdesc *d)
-{
- if (!d) {
- fprintf(file,"(paramdesc *)NULL");
- return;
- }
-
- if (d->inmemory) {
- fprintf(file,"<m%d>",d->regoff);
- }
- else {
- fprintf(file,"<r%d>",d->regoff);
- }
-}
-
-/* descriptor_debug_print_methoddesc *******************************************
-
- Print the given methoddesc to the given stream
-
- IN:
- file.............stream to print to
- d................the parsed descriptor
-
-*******************************************************************************/
-
-void
-descriptor_debug_print_methoddesc(FILE *file,methoddesc *d)
-{
- int i;
-
- if (!d) {
- fprintf(file,"(methoddesc *)NULL");
- return;
- }
-
- fputc('(',file);
- for (i=0; i<d->paramcount; ++i) {
- if (i)
- fputc(',',file);
- descriptor_debug_print_typedesc(file,d->paramtypes + i);
- if (d->params) {
- descriptor_debug_print_paramdesc(file,d->params + i);
- }
- }
- if (d->params == METHODDESC_NOPARAMS)
- fputs("<NOPARAMS>",file);
- fputc(')',file);
- descriptor_debug_print_typedesc(file,&(d->returntype));
-}
-
-/* descriptor_pool_debug_dump **************************************************
-
- Print the state of the descriptor_pool to the given stream
-
- IN:
- pool.............the descriptor_pool
- file.............stream to print to
-
-*******************************************************************************/
-
-void
-descriptor_pool_debug_dump(descriptor_pool *pool,FILE *file)
-{
- u4 slot;
- u1 *pos;
- u1 *kind;
- u4 size;
-
- fprintf(file,"======[descriptor_pool for ");
- utf_fprint_printable_ascii(file,pool->referer->name);
- fprintf(file,"]======\n");
-
- fprintf(file,"fieldcount: %d\n",pool->fieldcount);
- fprintf(file,"methodcount: %d\n",pool->methodcount);
- fprintf(file,"paramcount: %d\n",pool->paramcount);
- fprintf(file,"classrefcount: %d\n",pool->classrefhash.entries);
- fprintf(file,"descriptorsize: %d bytes\n",pool->descriptorsize);
- fprintf(file,"classrefsize: %d bytes\n",
- (int)(pool->classrefhash.entries * sizeof(constant_classref)));
-
- fprintf(file,"class references:\n");
- for (slot=0; slot<pool->classrefhash.size; ++slot) {
- classref_hash_entry *c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
- while (c) {
- fprintf(file," %4d: ",c->index);
- utf_fprint_printable_ascii(file,c->name);
- fprintf(file,"\n");
- c = c->hashlink;
- }
- }
-
- fprintf(file,"hashed descriptors:\n");
- for (slot=0; slot<pool->descriptorhash.size; ++slot) {
- descriptor_hash_entry *c = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
- while (c) {
- fprintf(file," %p: ",c->parseddesc.any);
- utf_fprint_printable_ascii(file,c->desc);
- fprintf(file,"\n");
- c = c->hashlink;
- }
- }
-
- fprintf(file,"descriptors:\n");
- if (pool->descriptors) {
- pos = pool->descriptors;
- size = pool->descriptors_next - pool->descriptors;
- fprintf(file," size: %d bytes\n",size);
-
- if (pool->descriptor_kind) {
- kind = pool->descriptor_kind;
-
- while (pos < (pool->descriptors + size)) {
- fprintf(file," %p: ",pos);
- switch (*kind++) {
- case 'f':
- descriptor_debug_print_typedesc(file,(typedesc*)pos);
- pos += sizeof(typedesc);
- break;
- case 'm':
- descriptor_debug_print_methoddesc(file,(methoddesc*)pos);
- pos += ((methoddesc*)pos)->paramcount * sizeof(typedesc);
- pos += sizeof(methoddesc) - sizeof(typedesc);
- break;
- default:
- fprintf(file,"INVALID KIND");
- }
- fputc('\n',file);
- }
- }
- else {
- while (size >= sizeof(void*)) {
- fprintf(file," %p\n",*((void**)pos));
- pos += sizeof(void*);
- size -= sizeof(void*);
- }
- }
- }
-
- fprintf(file,"==========================================================\n");
-}
-#endif /* !defined(NDEBUG) */
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
-
--- /dev/null
+/* src/vm/descriptor.c - checking and parsing of field / method descriptors
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+
+#include "vm/types.h"
+
+#include "md-abi.h"
+
+#include "mm/memory.h"
+
+#include "vm/descriptor.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/options.h"
+#include "vm/primitive.hpp"
+#include "vm/vm.hpp"
+
+#include "vm/jit/abi.h"
+
+
+/* constants (private to descriptor.c) ****************************************/
+
+/* initial number of entries for the classrefhash of a descriptor_pool */
+/* (currently the hash is never grown!) */
+#define CLASSREFHASH_INIT_SIZE 64
+
+/* initial number of entries for the descriptorhash of a descriptor_pool */
+/* (currently the hash is never grown!) */
+#define DESCRIPTORHASH_INIT_SIZE 128
+
+/* data structures (private to descriptor.c) **********************************/
+
+typedef struct classref_hash_entry classref_hash_entry;
+typedef struct descriptor_hash_entry descriptor_hash_entry;
+
+/* entry struct for the classrefhash of descriptor_pool */
+struct classref_hash_entry {
+ classref_hash_entry *hashlink; /* for hash chaining */
+ utf *name; /* name of the class refered to */
+ u2 index; /* index into classref table */
+};
+
+/* entry struct for the descriptorhash of descriptor_pool */
+struct descriptor_hash_entry {
+ descriptor_hash_entry *hashlink;
+ utf *desc;
+ parseddesc_t parseddesc;
+ s2 paramslots; /* number of params, LONG/DOUBLE counted as 2 */
+};
+
+
+/****************************************************************************/
+/* MACROS FOR DESCRIPTOR PARSING (private to descriptor.c) */
+/****************************************************************************/
+
+/* SKIP_FIELDDESCRIPTOR:
+ * utf_ptr must point to the first character of a field descriptor.
+ * After the macro call utf_ptr points to the first character after
+ * the field descriptor.
+ *
+ * CAUTION: This macro does not check for an unexpected end of the
+ * descriptor. Better use SKIP_FIELDDESCRIPTOR_SAFE.
+ */
+#define SKIP_FIELDDESCRIPTOR(utf_ptr) \
+ do { while (*(utf_ptr)=='[') (utf_ptr)++; \
+ if (*(utf_ptr)++=='L') \
+ while(*(utf_ptr)++ != ';') /* skip */; } while(0)
+
+/* SKIP_FIELDDESCRIPTOR_SAFE:
+ * utf_ptr must point to the first character of a field descriptor.
+ * After the macro call utf_ptr points to the first character after
+ * the field descriptor.
+ *
+ * Input:
+ * utf_ptr....points to first char of descriptor
+ * end_ptr....points to first char after the end of the string
+ * errorflag..must be initialized (to false) by the caller!
+ * Output:
+ * utf_ptr....points to first char after the descriptor
+ * errorflag..set to true if the string ended unexpectedly
+ */
+#define SKIP_FIELDDESCRIPTOR_SAFE(utf_ptr,end_ptr,errorflag) \
+ do { while ((utf_ptr) != (end_ptr) && *(utf_ptr)=='[') (utf_ptr)++; \
+ if ((utf_ptr) == (end_ptr)) \
+ (errorflag) = true; \
+ else \
+ if (*(utf_ptr)++=='L') { \
+ while((utf_ptr) != (end_ptr) && *(utf_ptr)++ != ';') \
+ /* skip */; \
+ if ((utf_ptr)[-1] != ';') \
+ (errorflag) = true; }} while(0)
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/****************************************************************************/
+/* DEBUG HELPERS */
+/****************************************************************************/
+
+/*#define DESCRIPTOR_VERBOSE*/
+
+/****************************************************************************/
+/* FUNCTIONS */
+/****************************************************************************/
+
+/* descriptor_to_basic_type ****************************************************
+
+ Return the basic type to use for a value with this descriptor.
+
+ IN:
+ utf..............descriptor utf string
+
+ OUT:
+ A TYPE_* constant.
+
+ PRECONDITIONS:
+ This function assumes that the descriptor has passed
+ descriptor_pool_add checks and that it does not start with '('.
+
+*******************************************************************************/
+
+int descriptor_to_basic_type(utf *descriptor)
+{
+ assert(descriptor->blength >= 1);
+
+ switch (descriptor->text[0]) {
+ case 'Z':
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'I':
+ return TYPE_INT;
+
+ case 'J':
+ return TYPE_LNG;
+
+ case 'F':
+ return TYPE_FLT;
+
+ case 'D':
+ return TYPE_DBL;
+
+ case 'L':
+ case '[':
+ return TYPE_ADR;
+
+ default:
+ vm_abort("descriptor_to_basic_type: invalid type %c",
+ descriptor->text[0]);
+ }
+
+ /* keep the compiler happy */
+
+ return 0;
+}
+
+
+/* descriptor_typesize *********************************************************
+
+ Return the size in bytes needed for the given type.
+
+ IN:
+ td..............typedesc describing the type
+
+ OUT:
+ The number of bytes
+
+*******************************************************************************/
+
+int descriptor_typesize(typedesc *td)
+{
+ assert(td);
+
+ switch (td->type) {
+ case TYPE_INT:
+ case TYPE_FLT:
+ return 4;
+
+ case TYPE_LNG:
+ case TYPE_DBL:
+ return 8;
+
+ case TYPE_ADR:
+ return SIZEOF_VOID_P;
+
+ default:
+ vm_abort("descriptor_typesize: invalid type %d", td->type);
+ }
+
+ /* keep the compiler happy */
+
+ return 0;
+}
+
+
+/* name_from_descriptor ********************************************************
+
+ Return the class name indicated by the given descriptor
+ (Internally used helper function)
+
+ IN:
+ c................class containing the descriptor
+ utf_ptr..........first character of descriptor
+ end_ptr..........first character after the end of the string
+ mode.............a combination (binary or) of the following flags:
+
+ (Flags marked with * are the default settings.)
+
+ How to handle "V" descriptors:
+
+ * DESCRIPTOR_VOID.....handle it like other primitive types
+ DESCRIPTOR_NOVOID...treat it as an error
+
+ How to deal with extra characters after the end of the
+ descriptor:
+
+ * DESCRIPTOR_NOCHECKEND...ignore (useful for parameter lists)
+ DESCRIPTOR_CHECKEND.....treat them as an error
+
+ OUT:
+ *next............if non-NULL, *next is set to the first character after
+ the descriptor. (Undefined if an error occurs.)
+ *name............set to the utf name of the class
+
+ RETURN VALUE:
+ true.............descriptor parsed successfully
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+#define DESCRIPTOR_VOID 0 /* default */
+#define DESCRIPTOR_NOVOID 0x0040
+#define DESCRIPTOR_NOCHECKEND 0 /* default */
+#define DESCRIPTOR_CHECKEND 0x1000
+
+static bool
+name_from_descriptor(classinfo *c,
+ char *utf_ptr, char *end_ptr,
+ char **next, int mode, utf **name)
+{
+ char *start = utf_ptr;
+ bool error = false;
+
+ assert(c);
+ assert(utf_ptr);
+ assert(end_ptr);
+ assert(name);
+
+ *name = NULL;
+ SKIP_FIELDDESCRIPTOR_SAFE(utf_ptr, end_ptr, error);
+
+ if (mode & DESCRIPTOR_CHECKEND)
+ error |= (utf_ptr != end_ptr);
+
+ if (!error) {
+ if (next) *next = utf_ptr;
+
+ switch (*start) {
+ case 'V':
+ if (mode & DESCRIPTOR_NOVOID)
+ break;
+ /* FALLTHROUGH! */
+ case 'I':
+ case 'J':
+ case 'F':
+ case 'D':
+ case 'B':
+ case 'C':
+ case 'S':
+ case 'Z':
+ return true;
+
+ case 'L':
+ start++;
+ utf_ptr--;
+ /* FALLTHROUGH! */
+ case '[':
+ *name = utf_new(start, utf_ptr - start);
+ return true;
+ }
+ }
+
+ exceptions_throw_classformaterror(c, "Invalid descriptor");
+ return false;
+}
+
+
+/* descriptor_to_typedesc ******************************************************
+
+ Parse the given type descriptor and fill a typedesc struct
+ (Internally used helper function)
+
+ IN:
+ pool.............the descriptor pool
+ utf_ptr..........points to first character of type descriptor
+ end_pos..........points after last character of the whole descriptor
+
+ OUT:
+ *next............set to next character after type descriptor
+ *d...............filled with parsed information
+
+ RETURN VALUE:
+ true.............parsing succeeded
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+static bool
+descriptor_to_typedesc(descriptor_pool *pool, char *utf_ptr, char *end_pos,
+ char **next, typedesc *td)
+{
+ utf *name;
+
+ if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, next, 0, &name))
+ return false;
+
+ if (name) {
+ /* a reference type */
+ td->type = TYPE_ADR;
+ td->primitivetype = TYPE_ADR;
+ td->arraydim = 0;
+ for (utf_ptr = name->text; *utf_ptr == '['; ++utf_ptr)
+ td->arraydim++;
+ td->classref = descriptor_pool_lookup_classref(pool, name);
+
+ } else {
+ /* a primitive type */
+ switch (*utf_ptr) {
+ case 'B':
+ td->primitivetype = PRIMITIVETYPE_BYTE;
+ td->type = TYPE_INT;
+ break;
+ case 'C':
+ td->primitivetype = PRIMITIVETYPE_CHAR;
+ td->type = TYPE_INT;
+ break;
+ case 'S':
+ td->primitivetype = PRIMITIVETYPE_SHORT;
+ td->type = TYPE_INT;
+ break;
+ case 'Z':
+ td->primitivetype = PRIMITIVETYPE_BOOLEAN;
+ td->type = TYPE_INT;
+ break;
+ case 'I':
+ td->primitivetype = PRIMITIVETYPE_INT;
+ td->type = TYPE_INT;
+ break;
+ case 'D':
+ td->primitivetype = PRIMITIVETYPE_DOUBLE;
+ td->type = TYPE_DBL;
+ break;
+ case 'F':
+ td->primitivetype = PRIMITIVETYPE_FLOAT;
+ td->type = TYPE_FLT;
+ break;
+ case 'J':
+ td->primitivetype = PRIMITIVETYPE_LONG;
+ td->type = TYPE_LNG;
+ break;
+ case 'V':
+ td->primitivetype = PRIMITIVETYPE_VOID;
+ td->type = TYPE_VOID;
+ break;
+ default:
+ assert(false);
+ }
+
+ td->arraydim = 0;
+ td->classref = NULL;
+ }
+
+ return true;
+}
+
+
+/* descriptor_pool_new *********************************************************
+
+ Allocate a new descriptor_pool
+
+ IN:
+ referer..........class for which to create the pool
+
+ RETURN VALUE:
+ a pointer to the new descriptor_pool
+
+*******************************************************************************/
+
+descriptor_pool *
+descriptor_pool_new(classinfo *referer)
+{
+ descriptor_pool *pool;
+ u4 hashsize;
+ u4 slot;
+
+ pool = (descriptor_pool*) DumpMemory::allocate(sizeof(descriptor_pool));
+ assert(pool);
+
+ pool->referer = referer;
+ pool->fieldcount = 0;
+ pool->methodcount = 0;
+ pool->paramcount = 0;
+ pool->descriptorsize = 0;
+ pool->descriptors = NULL;
+ pool->descriptors_next = NULL;
+ pool->classrefs = NULL;
+ pool->descriptor_kind = NULL;
+ pool->descriptor_kind_next = NULL;
+
+ hashsize = CLASSREFHASH_INIT_SIZE;
+ pool->classrefhash.size = hashsize;
+ pool->classrefhash.entries = 0;
+ pool->classrefhash.ptr = (void **) DumpMemory::allocate(sizeof(void*) * hashsize);
+ for (slot=0; slot<hashsize; ++slot)
+ pool->classrefhash.ptr[slot] = NULL;
+
+ hashsize = DESCRIPTORHASH_INIT_SIZE;
+ pool->descriptorhash.size = hashsize;
+ pool->descriptorhash.entries = 0;
+ pool->descriptorhash.ptr = (void**) DumpMemory::allocate(sizeof(void*) * hashsize);
+ for (slot=0; slot<hashsize; ++slot)
+ pool->descriptorhash.ptr[slot] = NULL;
+
+ return pool;
+}
+
+
+/* descriptor_pool_add_class ***************************************************
+
+ Add the given class reference to the pool
+
+ IN:
+ pool.............the descriptor_pool
+ name.............the class reference to add
+
+ RETURN VALUE:
+ true.............reference has been added
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool
+descriptor_pool_add_class(descriptor_pool *pool, utf *name)
+{
+ u4 key,slot;
+ classref_hash_entry *c;
+
+ assert(pool);
+ assert(name);
+
+#ifdef DESCRIPTOR_VERBOSE
+ fprintf(stderr,"descriptor_pool_add_class(%p,",(void*)pool);
+ utf_fprint_printable_ascii(stderr,name);fprintf(stderr,")\n");
+#endif
+
+ /* find a place in the hashtable */
+
+ key = utf_hashkey(name->text, name->blength);
+ slot = key & (pool->classrefhash.size - 1);
+ c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
+
+ while (c) {
+ if (c->name == name)
+ return true; /* already stored */
+ c = c->hashlink;
+ }
+
+ /* check if the name is a valid classname */
+
+ if (!is_valid_name(name->text,UTF_END(name))) {
+ exceptions_throw_classformaterror(pool->referer, "Invalid class name");
+ return false; /* exception */
+ }
+
+ /* XXX check maximum array dimension */
+
+ c = (classref_hash_entry*) DumpMemory::allocate(sizeof(classref_hash_entry));
+ c->name = name;
+ c->index = pool->classrefhash.entries++;
+ c->hashlink = (classref_hash_entry *) pool->classrefhash.ptr[slot];
+ pool->classrefhash.ptr[slot] = c;
+
+ return true;
+}
+
+
+/* descriptor_pool_add *********************************************************
+
+ Check the given descriptor and add it to the pool
+
+ IN:
+ pool.............the descriptor_pool
+ desc.............the descriptor to add. Maybe a field or method desc.
+
+ OUT:
+ *paramslots......if non-NULL, set to the number of parameters.
+ LONG and DOUBLE are counted twice
+
+ RETURN VALUE:
+ true.............descriptor has been added
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool
+descriptor_pool_add(descriptor_pool *pool, utf *desc, int *paramslots)
+{
+ u4 key,slot;
+ descriptor_hash_entry *d;
+ char *utf_ptr;
+ char *end_pos;
+ utf *name;
+ s4 argcount = 0;
+
+#ifdef DESCRIPTOR_VERBOSE
+ fprintf(stderr,"descriptor_pool_add(%p,",(void*)pool);
+ utf_fprint_printable_ascii(stderr,desc);fprintf(stderr,")\n");
+#endif
+
+ assert(pool);
+ assert(desc);
+
+ /* find a place in the hashtable */
+
+ key = utf_hashkey(desc->text, desc->blength);
+ slot = key & (pool->descriptorhash.size - 1);
+ d = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
+
+ /* Save all method descriptors in the hashtable, since the parsed */
+ /* descriptor may vary between differenf methods (static vs. non-static). */
+
+ utf_ptr = desc->text;
+
+ if (*utf_ptr != '(') {
+ while (d) {
+ if (d->desc == desc) {
+ if (paramslots)
+ *paramslots = d->paramslots;
+ return true; /* already stored */
+ }
+ d = d->hashlink;
+ }
+ }
+
+ /* add the descriptor to the pool */
+
+ d = (descriptor_hash_entry*) DumpMemory::allocate(sizeof(descriptor_hash_entry));
+ d->desc = desc;
+ d->parseddesc.any = NULL;
+ d->hashlink = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
+ pool->descriptorhash.ptr[slot] = d;
+
+ /* now check the descriptor */
+
+ end_pos = UTF_END(desc);
+
+ if (*utf_ptr == '(') {
+ /* a method descriptor */
+
+ pool->methodcount++;
+ utf_ptr++;
+
+ /* check arguments */
+
+ while ((utf_ptr != end_pos) && (*utf_ptr != ')')) {
+ pool->paramcount++;
+
+ /* We cannot count the `this' argument here because
+ * we don't know if the method is static. */
+
+ if (*utf_ptr == 'J' || *utf_ptr == 'D')
+ argcount += 2;
+ else
+ argcount++;
+
+ if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, &utf_ptr,
+ DESCRIPTOR_NOVOID, &name))
+ return false;
+
+ if (name)
+ if (!descriptor_pool_add_class(pool, name))
+ return false;
+ }
+
+ if (utf_ptr == end_pos) {
+ exceptions_throw_classformaterror(pool->referer,
+ "Missing ')' in method descriptor");
+ return false;
+ }
+
+ utf_ptr++; /* skip ')' */
+
+ if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, NULL,
+ DESCRIPTOR_CHECKEND, &name))
+ return false;
+
+ if (name)
+ if (!descriptor_pool_add_class(pool,name))
+ return false;
+
+ if (argcount > 255) {
+ exceptions_throw_classformaterror(pool->referer,
+ "Too many arguments in signature");
+ return false;
+ }
+
+ } else {
+ /* a field descriptor */
+
+ pool->fieldcount++;
+
+ if (!name_from_descriptor(pool->referer, utf_ptr, end_pos, NULL,
+ DESCRIPTOR_NOVOID | DESCRIPTOR_CHECKEND,
+ &name))
+ return false;
+
+ if (name)
+ if (!descriptor_pool_add_class(pool,name))
+ return false;
+ }
+
+ d->paramslots = argcount;
+
+ if (paramslots)
+ *paramslots = argcount;
+
+ return true;
+}
+
+
+/* descriptor_pool_create_classrefs ********************************************
+
+ Create a table containing all the classrefs which were added to the pool
+
+ IN:
+ pool.............the descriptor_pool
+
+ OUT:
+ *count...........if count is non-NULL, this is set to the number
+ of classrefs in the table
+
+ RETURN VALUE:
+ a pointer to the constant_classref table
+
+*******************************************************************************/
+
+constant_classref *
+descriptor_pool_create_classrefs(descriptor_pool *pool, s4 *count)
+{
+ u4 nclasses;
+ u4 slot;
+ classref_hash_entry *c;
+ constant_classref *ref;
+
+ assert(pool);
+
+ nclasses = pool->classrefhash.entries;
+ pool->classrefs = MNEW(constant_classref,nclasses);
+
+ /* fill the constant_classref structs */
+
+ for (slot = 0; slot < pool->classrefhash.size; ++slot) {
+ c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
+ while (c) {
+ ref = pool->classrefs + c->index;
+ CLASSREF_INIT(*ref, pool->referer, c->name);
+ c = c->hashlink;
+ }
+ }
+
+ if (count)
+ *count = nclasses;
+
+ return pool->classrefs;
+}
+
+
+/* descriptor_pool_lookup_classref *********************************************
+
+ Return the constant_classref for the given class name
+
+ IN:
+ pool.............the descriptor_pool
+ classname........name of the class to look up
+
+ RETURN VALUE:
+ a pointer to the constant_classref, or
+ NULL if an exception has been thrown
+
+*******************************************************************************/
+
+constant_classref *
+descriptor_pool_lookup_classref(descriptor_pool *pool, utf *classname)
+{
+ u4 key,slot;
+ classref_hash_entry *c;
+
+ assert(pool);
+ assert(pool->classrefs);
+ assert(classname);
+
+ key = utf_hashkey(classname->text, classname->blength);
+ slot = key & (pool->classrefhash.size - 1);
+ c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
+
+ while (c) {
+ if (c->name == classname)
+ return pool->classrefs + c->index;
+ c = c->hashlink;
+ }
+
+ exceptions_throw_internalerror("Class reference not found in descriptor pool");
+ return NULL;
+}
+
+
+/* descriptor_pool_alloc_parsed_descriptors ************************************
+
+ Allocate space for the parsed descriptors
+
+ IN:
+ pool.............the descriptor_pool
+
+ NOTE:
+ This function must be called after all descriptors have been added
+ with descriptor_pool_add.
+
+*******************************************************************************/
+
+void
+descriptor_pool_alloc_parsed_descriptors(descriptor_pool *pool)
+{
+ u4 size;
+
+ assert(pool);
+
+ /* TWISTI: paramcount + 1: we don't know if the method is static or */
+ /* not, i have no better solution yet. */
+
+ size =
+ pool->fieldcount * sizeof(typedesc) +
+ pool->methodcount * (sizeof(methoddesc) - sizeof(typedesc)) +
+ pool->paramcount * sizeof(typedesc) +
+ pool->methodcount * sizeof(typedesc); /* possible `this' pointer */
+
+ pool->descriptorsize = size;
+ if (size) {
+ pool->descriptors = MNEW(u1, size);
+ pool->descriptors_next = pool->descriptors;
+ }
+
+ size = pool->fieldcount + pool->methodcount;
+ if (size) {
+ pool->descriptor_kind = (u1*) DumpMemory::allocate(sizeof(u1) * size);
+ pool->descriptor_kind_next = pool->descriptor_kind;
+ }
+}
+
+
+/* descriptor_pool_parse_field_descriptor **************************************
+
+ Parse the given field descriptor
+
+ IN:
+ pool.............the descriptor_pool
+ desc.............the field descriptor
+
+ RETURN VALUE:
+ a pointer to the parsed field descriptor, or
+ NULL if an exception has been thrown
+
+ NOTE:
+ descriptor_pool_alloc_parsed_descriptors must be called (once)
+ before this function is used.
+
+*******************************************************************************/
+
+typedesc *
+descriptor_pool_parse_field_descriptor(descriptor_pool *pool, utf *desc)
+{
+ u4 key,slot;
+ descriptor_hash_entry *d;
+ typedesc *td;
+
+ assert(pool);
+ assert(pool->descriptors);
+ assert(pool->descriptors_next);
+
+ /* lookup the descriptor in the hashtable */
+
+ key = utf_hashkey(desc->text, desc->blength);
+ slot = key & (pool->descriptorhash.size - 1);
+ d = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
+
+ while (d) {
+ if (d->desc == desc) {
+ /* found */
+ if (d->parseddesc.fd)
+ return d->parseddesc.fd;
+ break;
+ }
+ d = d->hashlink;
+ }
+
+ assert(d);
+
+ if (desc->text[0] == '(') {
+ exceptions_throw_classformaterror(pool->referer,
+ "Method descriptor used in field reference");
+ return NULL;
+ }
+
+ td = (typedesc *) pool->descriptors_next;
+ pool->descriptors_next += sizeof(typedesc);
+
+ if (!descriptor_to_typedesc(pool, desc->text, UTF_END(desc), NULL, td))
+ return NULL;
+
+ *(pool->descriptor_kind_next++) = 'f';
+
+ d->parseddesc.fd = td;
+
+ return td;
+}
+
+
+/* descriptor_pool_parse_method_descriptor *************************************
+
+ Parse the given method descriptor
+
+ IN:
+ pool.............the descriptor_pool
+ desc.............the method descriptor
+ mflags...........the method flags
+ thisclass........classref to the class containing the method.
+ This is ignored if mflags contains ACC_STATIC.
+ The classref is stored for inserting the 'this' argument.
+
+ RETURN VALUE:
+ a pointer to the parsed method descriptor, or
+ NULL if an exception has been thrown
+
+ NOTE:
+ descriptor_pool_alloc_parsed_descriptors must be called
+ (once) before this function is used.
+
+*******************************************************************************/
+
+methoddesc *
+descriptor_pool_parse_method_descriptor(descriptor_pool *pool, utf *desc,
+ s4 mflags,constant_classref *thisclass)
+{
+ u4 key, slot;
+ descriptor_hash_entry *d;
+ methoddesc *md;
+ typedesc *td;
+ char *utf_ptr;
+ char *end_pos;
+ s2 paramcount = 0;
+ s2 paramslots = 0;
+
+#ifdef DESCRIPTOR_VERBOSE
+ fprintf(stderr,"descriptor_pool_parse_method_descriptor(%p,%d,%p,",
+ (void*)pool,(int)mflags,(void*)thisclass);
+ utf_fprint_printable_ascii(stderr,desc); fprintf(stderr,")\n");
+#endif
+
+ assert(pool);
+ assert(pool->descriptors);
+ assert(pool->descriptors_next);
+
+ /* check that it is a method descriptor */
+
+ if (desc->text[0] != '(') {
+ exceptions_throw_classformaterror(pool->referer,
+ "Field descriptor used in method reference");
+ return NULL;
+ }
+
+ /* lookup the descriptor in the hashtable */
+
+ key = utf_hashkey(desc->text, desc->blength);
+ slot = key & (pool->descriptorhash.size - 1);
+ d = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
+
+ /* find an un-parsed descriptor */
+
+ while (d) {
+ if (d->desc == desc)
+ if (!d->parseddesc.md)
+ break;
+ d = d->hashlink;
+ }
+
+ assert(d);
+
+ md = (methoddesc *) pool->descriptors_next;
+ pool->descriptors_next += sizeof(methoddesc) - sizeof(typedesc);
+
+ utf_ptr = desc->text + 1; /* skip '(' */
+ end_pos = UTF_END(desc);
+
+ td = md->paramtypes;
+
+ /* count the `this' pointer */
+
+ if ((mflags != ACC_UNDEF) && !(mflags & ACC_STATIC)) {
+ td->type = TYPE_ADR;
+ td->primitivetype = TYPE_ADR;
+ td->arraydim = 0;
+ td->classref = thisclass;
+
+ td++;
+ pool->descriptors_next += sizeof(typedesc);
+ paramcount++;
+ paramslots++;
+ }
+
+ while (*utf_ptr != ')') {
+ /* parse a parameter type */
+
+ if (!descriptor_to_typedesc(pool, utf_ptr, end_pos, &utf_ptr, td))
+ return NULL;
+
+ if (IS_2_WORD_TYPE(td->type))
+ paramslots++;
+
+ td++;
+ pool->descriptors_next += sizeof(typedesc);
+ paramcount++;
+ paramslots++;
+ }
+ utf_ptr++; /* skip ')' */
+
+ /* Skip possible `this' pointer in paramtypes array to allow a possible */
+ /* memory move later in parse. */
+ /* We store the thisclass reference, so we can later correctly fill in */
+ /* the parameter slot of the 'this' argument. */
+
+ if (mflags == ACC_UNDEF) {
+ td->classref = thisclass;
+ td++;
+ pool->descriptors_next += sizeof(typedesc);
+ }
+
+ /* parse return type */
+
+ if (!descriptor_to_typedesc(pool, utf_ptr, end_pos, NULL,
+ &(md->returntype)))
+ return NULL;
+
+ md->paramcount = paramcount;
+ md->paramslots = paramslots;
+
+ /* If mflags != ACC_UNDEF we parse a real loaded method, so do
+ param prealloc. Otherwise we do this in stack analysis. */
+
+ if (mflags != ACC_UNDEF) {
+ if (md->paramcount > 0) {
+ /* allocate memory for params */
+
+ md->params = MNEW(paramdesc, md->paramcount);
+ }
+ else {
+ md->params = METHODDESC_NOPARAMS;
+ }
+
+ /* fill the paramdesc */
+ /* md_param_alloc has to be called if md->paramcount == 0,
+ too, so it can make the reservation for the Linkage Area,
+ Return Register... */
+
+#if defined(ENABLE_JIT)
+# if defined(ENABLE_INTRP)
+ if (!opt_intrp)
+# endif
+ {
+ /* As builtin-functions are native functions, we have
+ to pre-allocate for the native ABI. */
+
+ if (mflags & ACC_METHOD_BUILTIN)
+ md_param_alloc_native(md);
+ else
+ md_param_alloc(md);
+ }
+#endif
+ }
+ else {
+ /* params will be allocated later by
+ descriptor_params_from_paramtypes if necessary */
+
+ md->params = NULL;
+ }
+
+ *(pool->descriptor_kind_next++) = 'm';
+
+ d->parseddesc.md = md;
+
+ return md;
+}
+
+/* descriptor_params_from_paramtypes *******************************************
+
+ Create the paramdescs for a method descriptor. This function is called
+ when we know whether the method is static or not. This function may only
+ be called once for each methoddesc, and only if md->params == NULL.
+
+ IN:
+ md...............the parsed method descriptor
+ md->params MUST be NULL.
+ mflags...........the ACC_* access flags of the method. Only the
+ ACC_STATIC bit is checked.
+ The value ACC_UNDEF is NOT allowed.
+
+ RETURN VALUE:
+ true.............the paramdescs were created successfully
+ false............an exception has been thrown
+
+ POSTCONDITION:
+ md->parms != NULL
+
+*******************************************************************************/
+
+bool descriptor_params_from_paramtypes(methoddesc *md, s4 mflags)
+{
+ typedesc *td;
+
+ assert(md);
+ assert(md->params == NULL);
+ assert(mflags != ACC_UNDEF);
+
+ td = md->paramtypes;
+
+ /* check for `this' pointer */
+
+ if (!(mflags & ACC_STATIC)) {
+ constant_classref *thisclass;
+
+ /* fetch class reference from reserved param slot */
+ thisclass = td[md->paramcount].classref;
+ assert(thisclass);
+
+ if (md->paramcount > 0) {
+ /* shift param types by 1 argument */
+ MMOVE(td + 1, td, typedesc, md->paramcount);
+ }
+
+ /* fill in first argument `this' */
+
+ td->type = TYPE_ADR;
+ td->primitivetype = TYPE_ADR;
+ td->arraydim = 0;
+ td->classref = thisclass;
+
+ md->paramcount++;
+ md->paramslots++;
+ }
+
+ /* if the method has params, process them */
+
+ if (md->paramcount > 0) {
+ /* allocate memory for params */
+
+ md->params = MNEW(paramdesc, md->paramcount);
+
+ } else {
+ md->params = METHODDESC_NOPARAMS;
+ }
+
+ /* fill the paramdesc */
+ /* md_param_alloc has to be called if md->paramcount == 0, too, so
+ it can make the reservation for the Linkage Area, Return
+ Register.. */
+
+#if defined(ENABLE_JIT)
+# if defined(ENABLE_INTRP)
+ if (!opt_intrp)
+# endif
+ {
+ /* As builtin-functions are native functions, we have to
+ pre-allocate for the native ABI. */
+
+ if (mflags & ACC_METHOD_BUILTIN)
+ md_param_alloc_native(md);
+ else
+ md_param_alloc(md);
+ }
+#endif
+
+ return true;
+}
+
+
+/* descriptor_pool_get_parsed_descriptors **************************************
+
+ Return a pointer to the block of parsed descriptors
+
+ IN:
+ pool.............the descriptor_pool
+
+ OUT:
+ *size............if size is non-NULL, this is set to the size of the
+ parsed descriptor block (in u1)
+
+ RETURN VALUE:
+ a pointer to the block of parsed descriptors
+
+ NOTE:
+ descriptor_pool_alloc_parsed_descriptors must be called (once)
+ before this function is used.
+
+*******************************************************************************/
+
+void *
+descriptor_pool_get_parsed_descriptors(descriptor_pool *pool, s4 *size)
+{
+ assert(pool);
+ assert((!pool->fieldcount && !pool->methodcount) || pool->descriptors);
+
+ if (size)
+ *size = pool->descriptorsize;
+
+ return pool->descriptors;
+}
+
+
+/* descriptor_pool_get_sizes ***************************************************
+
+ Get the sizes of the class reference table and the parsed descriptors
+
+ IN:
+ pool.............the descriptor_pool
+
+ OUT:
+ *classrefsize....set to size of the class reference table
+ *descsize........set to size of the parsed descriptors
+
+ NOTE:
+ This function may only be called after both
+ descriptor_pool_create_classrefs, and
+ descriptor_pool_alloc_parsed_descriptors
+ have been called.
+
+*******************************************************************************/
+
+void
+descriptor_pool_get_sizes(descriptor_pool *pool, u4 *classrefsize, u4 *descsize)
+{
+ assert(pool);
+ assert((!pool->fieldcount && !pool->methodcount) || pool->descriptors);
+ assert(pool->classrefs);
+ assert(classrefsize);
+ assert(descsize);
+
+ *classrefsize = pool->classrefhash.entries * sizeof(constant_classref);
+ *descsize = pool->descriptorsize;
+}
+
+
+/****************************************************************************/
+/* DEBUG HELPERS */
+/****************************************************************************/
+
+#ifndef NDEBUG
+/* descriptor_debug_print_typedesc *********************************************
+
+ Print the given typedesc to the given stream
+
+ IN:
+ file.............stream to print to
+ d................the parsed descriptor
+
+*******************************************************************************/
+
+void
+descriptor_debug_print_typedesc(FILE *file,typedesc *d)
+{
+ int ch;
+
+ if (!d) {
+ fprintf(file,"(typedesc *)NULL");
+ return;
+ }
+
+ if (d->type == TYPE_ADR) {
+ if (d->classref)
+ utf_fprint_printable_ascii(file,d->classref->name);
+ else
+ fprintf(file,"<class=NULL>");
+ }
+ else {
+ switch (d->primitivetype) {
+ case PRIMITIVETYPE_INT : ch='I'; break;
+ case PRIMITIVETYPE_CHAR : ch='C'; break;
+ case PRIMITIVETYPE_BYTE : ch='B'; break;
+ case PRIMITIVETYPE_SHORT : ch='S'; break;
+ case PRIMITIVETYPE_BOOLEAN: ch='Z'; break;
+ case PRIMITIVETYPE_LONG : ch='J'; break;
+ case PRIMITIVETYPE_FLOAT : ch='F'; break;
+ case PRIMITIVETYPE_DOUBLE : ch='D'; break;
+ case PRIMITIVETYPE_VOID : ch='V'; break;
+ default : ch='!';
+ }
+ fputc(ch,file);
+ }
+ if (d->arraydim)
+ fprintf(file,"[%d]",d->arraydim);
+}
+
+/* descriptor_debug_print_paramdesc ********************************************
+
+ Print the given paramdesc to the given stream
+
+ IN:
+ file.............stream to print to
+ d................the parameter descriptor
+
+*******************************************************************************/
+
+void
+descriptor_debug_print_paramdesc(FILE *file,paramdesc *d)
+{
+ if (!d) {
+ fprintf(file,"(paramdesc *)NULL");
+ return;
+ }
+
+ if (d->inmemory) {
+ fprintf(file,"<m%d>",d->regoff);
+ }
+ else {
+ fprintf(file,"<r%d>",d->regoff);
+ }
+}
+
+/* descriptor_debug_print_methoddesc *******************************************
+
+ Print the given methoddesc to the given stream
+
+ IN:
+ file.............stream to print to
+ d................the parsed descriptor
+
+*******************************************************************************/
+
+void
+descriptor_debug_print_methoddesc(FILE *file,methoddesc *d)
+{
+ int i;
+
+ if (!d) {
+ fprintf(file,"(methoddesc *)NULL");
+ return;
+ }
+
+ fputc('(',file);
+ for (i=0; i<d->paramcount; ++i) {
+ if (i)
+ fputc(',',file);
+ descriptor_debug_print_typedesc(file,d->paramtypes + i);
+ if (d->params) {
+ descriptor_debug_print_paramdesc(file,d->params + i);
+ }
+ }
+ if (d->params == METHODDESC_NOPARAMS)
+ fputs("<NOPARAMS>",file);
+ fputc(')',file);
+ descriptor_debug_print_typedesc(file,&(d->returntype));
+}
+
+/* descriptor_pool_debug_dump **************************************************
+
+ Print the state of the descriptor_pool to the given stream
+
+ IN:
+ pool.............the descriptor_pool
+ file.............stream to print to
+
+*******************************************************************************/
+
+void
+descriptor_pool_debug_dump(descriptor_pool *pool,FILE *file)
+{
+ u4 slot;
+ u1 *pos;
+ u1 *kind;
+ u4 size;
+
+ fprintf(file,"======[descriptor_pool for ");
+ utf_fprint_printable_ascii(file,pool->referer->name);
+ fprintf(file,"]======\n");
+
+ fprintf(file,"fieldcount: %d\n",pool->fieldcount);
+ fprintf(file,"methodcount: %d\n",pool->methodcount);
+ fprintf(file,"paramcount: %d\n",pool->paramcount);
+ fprintf(file,"classrefcount: %d\n",pool->classrefhash.entries);
+ fprintf(file,"descriptorsize: %d bytes\n",pool->descriptorsize);
+ fprintf(file,"classrefsize: %d bytes\n",
+ (int)(pool->classrefhash.entries * sizeof(constant_classref)));
+
+ fprintf(file,"class references:\n");
+ for (slot=0; slot<pool->classrefhash.size; ++slot) {
+ classref_hash_entry *c = (classref_hash_entry *) pool->classrefhash.ptr[slot];
+ while (c) {
+ fprintf(file," %4d: ",c->index);
+ utf_fprint_printable_ascii(file,c->name);
+ fprintf(file,"\n");
+ c = c->hashlink;
+ }
+ }
+
+ fprintf(file,"hashed descriptors:\n");
+ for (slot=0; slot<pool->descriptorhash.size; ++slot) {
+ descriptor_hash_entry *c = (descriptor_hash_entry *) pool->descriptorhash.ptr[slot];
+ while (c) {
+ fprintf(file," %p: ",c->parseddesc.any);
+ utf_fprint_printable_ascii(file,c->desc);
+ fprintf(file,"\n");
+ c = c->hashlink;
+ }
+ }
+
+ fprintf(file,"descriptors:\n");
+ if (pool->descriptors) {
+ pos = pool->descriptors;
+ size = pool->descriptors_next - pool->descriptors;
+ fprintf(file," size: %d bytes\n",size);
+
+ if (pool->descriptor_kind) {
+ kind = pool->descriptor_kind;
+
+ while (pos < (pool->descriptors + size)) {
+ fprintf(file," %p: ",pos);
+ switch (*kind++) {
+ case 'f':
+ descriptor_debug_print_typedesc(file,(typedesc*)pos);
+ pos += sizeof(typedesc);
+ break;
+ case 'm':
+ descriptor_debug_print_methoddesc(file,(methoddesc*)pos);
+ pos += ((methoddesc*)pos)->paramcount * sizeof(typedesc);
+ pos += sizeof(methoddesc) - sizeof(typedesc);
+ break;
+ default:
+ fprintf(file,"INVALID KIND");
+ }
+ fputc('\n',file);
+ }
+ }
+ else {
+ while (size >= sizeof(void*)) {
+ fprintf(file," %p\n",*((void**)pos));
+ pos += sizeof(void*);
+ size -= sizeof(void*);
+ }
+ }
+ }
+
+ fprintf(file,"==========================================================\n");
+}
+#endif /* !defined(NDEBUG) */
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+
+++ /dev/null
-/* src/vm/descriptor.h - checking and parsing of field / method descriptors
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _DESCRIPTOR_H
-#define _DESCRIPTOR_H
-
-/* forward typedefs ***********************************************************/
-
-typedef struct descriptor_pool descriptor_pool;
-typedef struct typedesc typedesc;
-typedef struct paramdesc paramdesc;
-typedef struct methoddesc methoddesc;
-
-
-#include "config.h"
-
-#include <stdint.h>
-
-#include "vm/types.h"
-
-#include "toolbox/hashtable.h"
-
-#include "vm/class.hpp"
-#include "vm/global.h"
-#include "vm/method.h"
-#include "vm/references.h"
-#include "vm/utf8.h"
-
-#include "arch.h" /* needed for HAS_ADDRESS_REGISTER_FILE */
-
-
-/* data structures ************************************************************/
-
-/*----------------------------------------------------------------------------*/
-/* Descriptor Pools */
-/* */
-/* A descriptor_pool is a temporary data structure used during loading of */
-/* a class. The descriptor_pool is used to allocate the table of */
-/* constant_classrefs the class uses, and for parsing the field and method */
-/* descriptors which occurr within the class. The inner workings of */
-/* descriptor_pool are not important for outside code. */
-/* */
-/* You use a descriptor_pool as follows: */
-/* */
-/* 1. create one with descriptor_pool_new */
-/* 2. add all explicit class references with descriptor_pool_add_class */
-/* 3. add all field/method descriptors with descriptor_pool_add */
-/* 4. call descriptor_pool_create_classrefs */
-/* You can now lookup classrefs with descriptor_pool_lookup_classref */
-/* 5. call descriptor_pool_alloc_parsed_descriptors */
-/* 6. for each field descriptor call descriptor_pool_parse_field_descriptor */
-/* for each method descriptor call descriptor_pool_parse_method_descriptor */
-/* 7. call descriptor_pool_get_parsed_descriptors */
-/* */
-/* IMPORTANT: The descriptor_pool functions use DNEW and DMNEW for allocating */
-/* memory which can be thrown away when the steps above have been */
-/* done. */
-/*----------------------------------------------------------------------------*/
-
-struct descriptor_pool {
- classinfo *referer;
- u4 fieldcount;
- u4 methodcount;
- u4 paramcount;
- u4 descriptorsize;
- u1 *descriptors;
- u1 *descriptors_next;
- hashtable descriptorhash;
- constant_classref *classrefs;
- hashtable classrefhash;
- u1 *descriptor_kind; /* useful for debugging */
- u1 *descriptor_kind_next; /* useful for debugging */
-};
-
-
-/* data structures for parsed field/method descriptors ************************/
-
-struct typedesc {
- constant_classref *classref; /* class reference for TYPE_ADR types */
- u1 type; /* TYPE_??? constant [1] */
- u1 primitivetype; /* (PRIMITIVE)TYPE_??? constant [2] */
- u1 arraydim; /* array dimension (0 if no array) */
-};
-
-/* [1]...the type field contains the basic type used within the VM. So ints, */
-/* shorts, chars, bytes, booleans all have TYPE_INT. */
-/* [2]...the primitivetype field contains the declared type. */
-/* So short is PRIMITIVETYPE_SHORT, char is PRIMITIVETYPE_CHAR. */
-/* For non-primitive types primitivetype is TYPE_ADR. */
-
-struct paramdesc {
-#if defined(__MIPS__)
- u1 type; /* TYPE_??? of the register allocated */
-#endif
- bool inmemory; /* argument in register or on stack */
- uint32_t index; /* index into argument register array */
- uint32_t regoff; /* register index or stack offset */
-};
-
-struct methoddesc {
- s2 paramcount; /* number of parameters */
- s2 paramslots; /* like above but LONG,DOUBLE count twice */
- s4 argintreguse; /* number of used integer argument registers */
- s4 argfltreguse; /* number of used float argument registers */
-#if defined(HAS_ADDRESS_REGISTER_FILE)
- s4 argadrreguse; /* number of used address registers */
-#endif
- s4 memuse; /* number of stack slots used */
- paramdesc *params; /* allocated parameter descriptions [3] */
- typedesc returntype; /* parsed descriptor of the return type */
- typedesc paramtypes[1]; /* parameter types, variable length! */
-};
-
-/* [3]...If params is NULL, the parameter descriptions have not yet been */
-/* allocated. In this case ___the possible 'this' pointer of the method */
-/* is NOT counted in paramcount/paramslots and it is NOT included in */
-/* the paramtypes array___. */
-/* If params != NULL, the parameter descriptions have been */
-/* allocated, and the 'this' pointer of the method, if any, IS included.*/
-/* In case the method has no parameters at all, the special value */
-/* METHODDESC_NO_PARAMS is used (see below). */
-
-/* METHODDESC_NO_PARAMS is a special value for the methoddesc.params field */
-/* indicating that the method is a static method without any parameters. */
-/* This special value must be != NULL and it may only be set if */
-/* md->paramcount == 0. */
-
-#define METHODDESC_NOPARAMS ((paramdesc*)1)
-
-/* function prototypes ********************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-descriptor_pool * descriptor_pool_new(classinfo *referer);
-
-bool descriptor_pool_add_class(descriptor_pool *pool,utf *name);
-bool descriptor_pool_add(descriptor_pool *pool,utf *desc,int *paramslots);
-
-int descriptor_to_basic_type(utf *desc);
-int descriptor_typesize(typedesc *td);
-
-constant_classref * descriptor_pool_create_classrefs(descriptor_pool *pool,
- s4 *count);
-constant_classref * descriptor_pool_lookup_classref(descriptor_pool *pool,utf *classname);
-
-void descriptor_pool_alloc_parsed_descriptors(descriptor_pool *pool);
-
-typedesc *descriptor_pool_parse_field_descriptor(descriptor_pool *pool, utf *desc);
-methoddesc *descriptor_pool_parse_method_descriptor(descriptor_pool *pool, utf *desc, s4 mflags,
- constant_classref *thisclass);
-
-bool descriptor_params_from_paramtypes(methoddesc *md, s4 mflags);
-
-void *descriptor_pool_get_parsed_descriptors(descriptor_pool *pool, s4 *size);
-void descriptor_pool_get_sizes(descriptor_pool *pool, u4 *classrefsize,
- u4 *descsize);
-
-#ifndef NDEBUG
-void descriptor_debug_print_typedesc(FILE *file,typedesc *d);
-void descriptor_debug_print_methoddesc(FILE *file,methoddesc *d);
-void descriptor_debug_print_paramdesc(FILE *file,paramdesc *d);
-void descriptor_pool_debug_dump(descriptor_pool *pool, FILE *file);
-#endif /* !defined(NDEBUG) */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _DESCRIPTOR_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/descriptor.h - checking and parsing of field / method descriptors
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _DESCRIPTOR_H
+#define _DESCRIPTOR_H
+
+/* forward typedefs ***********************************************************/
+
+typedef struct descriptor_pool descriptor_pool;
+typedef struct typedesc typedesc;
+typedef struct paramdesc paramdesc;
+typedef struct methoddesc methoddesc;
+
+
+#include "config.h"
+
+#include <stdint.h>
+
+#include "vm/types.h"
+
+#include "toolbox/hashtable.h"
+
+#include "vm/class.hpp"
+#include "vm/global.h"
+#include "vm/method.hpp"
+#include "vm/references.h"
+#include "vm/utf8.h"
+
+#include "arch.h" /* needed for HAS_ADDRESS_REGISTER_FILE */
+
+
+/* data structures ************************************************************/
+
+/*----------------------------------------------------------------------------*/
+/* Descriptor Pools */
+/* */
+/* A descriptor_pool is a temporary data structure used during loading of */
+/* a class. The descriptor_pool is used to allocate the table of */
+/* constant_classrefs the class uses, and for parsing the field and method */
+/* descriptors which occurr within the class. The inner workings of */
+/* descriptor_pool are not important for outside code. */
+/* */
+/* You use a descriptor_pool as follows: */
+/* */
+/* 1. create one with descriptor_pool_new */
+/* 2. add all explicit class references with descriptor_pool_add_class */
+/* 3. add all field/method descriptors with descriptor_pool_add */
+/* 4. call descriptor_pool_create_classrefs */
+/* You can now lookup classrefs with descriptor_pool_lookup_classref */
+/* 5. call descriptor_pool_alloc_parsed_descriptors */
+/* 6. for each field descriptor call descriptor_pool_parse_field_descriptor */
+/* for each method descriptor call descriptor_pool_parse_method_descriptor */
+/* 7. call descriptor_pool_get_parsed_descriptors */
+/* */
+/* IMPORTANT: The descriptor_pool functions use DNEW and DMNEW for allocating */
+/* memory which can be thrown away when the steps above have been */
+/* done. */
+/*----------------------------------------------------------------------------*/
+
+struct descriptor_pool {
+ classinfo *referer;
+ u4 fieldcount;
+ u4 methodcount;
+ u4 paramcount;
+ u4 descriptorsize;
+ u1 *descriptors;
+ u1 *descriptors_next;
+ hashtable descriptorhash;
+ constant_classref *classrefs;
+ hashtable classrefhash;
+ u1 *descriptor_kind; /* useful for debugging */
+ u1 *descriptor_kind_next; /* useful for debugging */
+};
+
+
+/* data structures for parsed field/method descriptors ************************/
+
+struct typedesc {
+ constant_classref *classref; /* class reference for TYPE_ADR types */
+ u1 type; /* TYPE_??? constant [1] */
+ u1 primitivetype; /* (PRIMITIVE)TYPE_??? constant [2] */
+ u1 arraydim; /* array dimension (0 if no array) */
+};
+
+/* [1]...the type field contains the basic type used within the VM. So ints, */
+/* shorts, chars, bytes, booleans all have TYPE_INT. */
+/* [2]...the primitivetype field contains the declared type. */
+/* So short is PRIMITIVETYPE_SHORT, char is PRIMITIVETYPE_CHAR. */
+/* For non-primitive types primitivetype is TYPE_ADR. */
+
+struct paramdesc {
+#if defined(__MIPS__)
+ u1 type; /* TYPE_??? of the register allocated */
+#endif
+ bool inmemory; /* argument in register or on stack */
+ uint32_t index; /* index into argument register array */
+ uint32_t regoff; /* register index or stack offset */
+};
+
+struct methoddesc {
+ s2 paramcount; /* number of parameters */
+ s2 paramslots; /* like above but LONG,DOUBLE count twice */
+ s4 argintreguse; /* number of used integer argument registers */
+ s4 argfltreguse; /* number of used float argument registers */
+#if defined(HAS_ADDRESS_REGISTER_FILE)
+ s4 argadrreguse; /* number of used address registers */
+#endif
+ s4 memuse; /* number of stack slots used */
+ paramdesc *params; /* allocated parameter descriptions [3] */
+ typedesc returntype; /* parsed descriptor of the return type */
+ typedesc paramtypes[1]; /* parameter types, variable length! */
+};
+
+/* [3]...If params is NULL, the parameter descriptions have not yet been */
+/* allocated. In this case ___the possible 'this' pointer of the method */
+/* is NOT counted in paramcount/paramslots and it is NOT included in */
+/* the paramtypes array___. */
+/* If params != NULL, the parameter descriptions have been */
+/* allocated, and the 'this' pointer of the method, if any, IS included.*/
+/* In case the method has no parameters at all, the special value */
+/* METHODDESC_NO_PARAMS is used (see below). */
+
+/* METHODDESC_NO_PARAMS is a special value for the methoddesc.params field */
+/* indicating that the method is a static method without any parameters. */
+/* This special value must be != NULL and it may only be set if */
+/* md->paramcount == 0. */
+
+#define METHODDESC_NOPARAMS ((paramdesc*)1)
+
+/* function prototypes ********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+descriptor_pool * descriptor_pool_new(classinfo *referer);
+
+bool descriptor_pool_add_class(descriptor_pool *pool,utf *name);
+bool descriptor_pool_add(descriptor_pool *pool,utf *desc,int *paramslots);
+
+int descriptor_to_basic_type(utf *desc);
+int descriptor_typesize(typedesc *td);
+
+constant_classref * descriptor_pool_create_classrefs(descriptor_pool *pool,
+ s4 *count);
+constant_classref * descriptor_pool_lookup_classref(descriptor_pool *pool,utf *classname);
+
+void descriptor_pool_alloc_parsed_descriptors(descriptor_pool *pool);
+
+typedesc *descriptor_pool_parse_field_descriptor(descriptor_pool *pool, utf *desc);
+methoddesc *descriptor_pool_parse_method_descriptor(descriptor_pool *pool, utf *desc, s4 mflags,
+ constant_classref *thisclass);
+
+bool descriptor_params_from_paramtypes(methoddesc *md, s4 mflags);
+
+void *descriptor_pool_get_parsed_descriptors(descriptor_pool *pool, s4 *size);
+void descriptor_pool_get_sizes(descriptor_pool *pool, u4 *classrefsize,
+ u4 *descsize);
+
+#ifndef NDEBUG
+void descriptor_debug_print_typedesc(FILE *file,typedesc *d);
+void descriptor_debug_print_methoddesc(FILE *file,methoddesc *d);
+void descriptor_debug_print_paramdesc(FILE *file,paramdesc *d);
+void descriptor_pool_debug_dump(descriptor_pool *pool, FILE *file);
+#endif /* !defined(NDEBUG) */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DESCRIPTOR_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
#include "vm/globals.hpp"
#include "vm/javaobjects.hpp"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/os.hpp"
#include "vm/string.hpp"
#include "vm/global.h"
#include "vm/references.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* function prototypes ********************************************************/
#include "vm/array.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/class.hpp"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/exceptions.hpp"
#include "vm/field.hpp"
#include "vm/global.h"
#include "config.h"
#include "vm/types.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/class.hpp"
#include "vm/global.h"
#include "vm/loader.hpp"
+++ /dev/null
-/* src/vm/finalizer.c - finalizer linked list and thread
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <stdlib.h>
-
-#include "vm/types.h"
-
-#include "mm/memory.h"
-
-#include "threads/condition.hpp"
-#include "threads/mutex.hpp"
-#include "threads/thread.hpp"
-
-#include "vm/jit/builtin.hpp"
-#include "vm/exceptions.hpp"
-#include "vm/global.h"
-#include "vm/options.h"
-#include "vm/vm.hpp"
-
-#include "vm/jit/asmpart.h"
-
-
-/* global variables ***********************************************************/
-
-#if defined(ENABLE_THREADS)
-static Mutex *finalizer_thread_mutex;
-static Condition *finalizer_thread_cond;
-#endif
-
-
-/* finalizer_init **************************************************************
-
- Initializes the finalizer global lock and the linked list.
-
-*******************************************************************************/
-
-bool finalizer_init(void)
-{
- TRACESUBSYSTEMINITIALIZATION("finalizer_init");
-
-#if defined(ENABLE_THREADS)
- finalizer_thread_mutex = Mutex_new();
- finalizer_thread_cond = Condition_new();
-#endif
-
- /* everything's ok */
-
- return true;
-}
-
-
-/* finalizer_thread ************************************************************
-
- This thread waits on an object for a notification and the runs the
- finalizers (finalizer thread). This is necessary because of a
- possible deadlock in the GC.
-
-*******************************************************************************/
-
-#if defined(ENABLE_THREADS)
-static void finalizer_thread(void)
-{
- while (true) {
- /* get the lock on the finalizer mutex, so we can call wait */
-
- Mutex_lock(finalizer_thread_mutex);
-
- /* wait forever on that condition till we are signaled */
-
- Condition_wait(finalizer_thread_cond, finalizer_thread_mutex);
-
- /* leave the lock */
-
- Mutex_unlock(finalizer_thread_mutex);
-
-#if !defined(NDEBUG)
- if (opt_DebugFinalizer)
- log_println("[finalizer thread : status=awake]");
-#endif
-
- /* and call the finalizers */
-
- gc_invoke_finalizers();
-
-#if !defined(NDEBUG)
- if (opt_DebugFinalizer)
- log_println("[finalizer thread : status=sleeping]");
-#endif
- }
-}
-#endif
-
-
-/* finalizer_start_thread ******************************************************
-
- Starts the finalizer thread.
-
-*******************************************************************************/
-
-#if defined(ENABLE_THREADS)
-bool finalizer_start_thread(void)
-{
- utf *name;
-
- name = utf_new_char("Finalizer");
-
- if (!threads_thread_start_internal(name, finalizer_thread))
- return false;
-
- /* everything's ok */
-
- return true;
-}
-#endif
-
-
-/* finalizer_notify ************************************************************
-
- Notifies the finalizer thread that it should run the
- gc_invoke_finalizers from the GC.
-
-*******************************************************************************/
-
-void finalizer_notify(void)
-{
-#if !defined(NDEBUG)
- if (opt_DebugFinalizer)
- log_println("[finalizer notified]");
-#endif
-
-#if defined(ENABLE_THREADS)
- /* get the lock on the finalizer lock object, so we can call wait */
-
- Mutex_lock(finalizer_thread_mutex);
-
- /* signal the finalizer thread */
-
- Condition_signal(finalizer_thread_cond);
-
- /* leave the lock */
-
- Mutex_unlock(finalizer_thread_mutex);
-#else
- /* if we don't have threads, just run the finalizers */
-
- gc_invoke_finalizers();
-#endif
-}
-
-
-/* finalizer_run ***************************************************************
-
- Actually run the finalizer functions.
-
-*******************************************************************************/
-
-void finalizer_run(void *o, void *p)
-{
- java_handle_t *h;
- classinfo *c;
-
- h = (java_handle_t *) o;
-
-#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
- /* XXX this is only a dirty hack to make Boehm work with handles */
-
- h = LLNI_WRAP((java_object_t *) h);
-#endif
-
- LLNI_class_get(h, c);
-
-#if !defined(NDEBUG)
- if (opt_DebugFinalizer) {
- log_start();
- log_print("[finalizer running : o=%p p=%p class=", o, p);
- class_print(c);
- log_print("]");
- log_finish();
- }
-#endif
-
- /* call the finalizer function */
-
- (void) vm_call_method(c->finalizer, h);
-
-#if !defined(NDEBUG)
- if (opt_DebugFinalizer && (exceptions_get_exception() != NULL)) {
- log_println("[finalizer exception]");
- exceptions_print_stacktrace();
- }
-#endif
-
- /* if we had an exception in the finalizer, ignore it */
-
- exceptions_clear_exception();
-}
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
--- /dev/null
+/* src/vm/finalizer.c - finalizer linked list and thread
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "threads/condition.hpp"
+#include "threads/mutex.hpp"
+#include "threads/thread.hpp"
+
+#include "vm/jit/builtin.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/global.h"
+#include "vm/options.h"
+#include "vm/vm.hpp"
+
+#include "vm/jit/asmpart.h"
+
+
+/* global variables ***********************************************************/
+
+#if defined(ENABLE_THREADS)
+static Mutex *finalizer_thread_mutex;
+static Condition *finalizer_thread_cond;
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* finalizer_init **************************************************************
+
+ Initializes the finalizer global lock and the linked list.
+
+*******************************************************************************/
+
+bool finalizer_init(void)
+{
+ TRACESUBSYSTEMINITIALIZATION("finalizer_init");
+
+#if defined(ENABLE_THREADS)
+ finalizer_thread_mutex = new Mutex();
+ finalizer_thread_cond = new Condition();
+#endif
+
+ /* everything's ok */
+
+ return true;
+}
+
+
+/* finalizer_thread ************************************************************
+
+ This thread waits on an object for a notification and the runs the
+ finalizers (finalizer thread). This is necessary because of a
+ possible deadlock in the GC.
+
+*******************************************************************************/
+
+#if defined(ENABLE_THREADS)
+static void finalizer_thread(void)
+{
+ while (true) {
+ /* get the lock on the finalizer mutex, so we can call wait */
+
+ finalizer_thread_mutex->lock();
+
+ /* wait forever on that condition till we are signaled */
+
+ finalizer_thread_cond->wait(finalizer_thread_mutex);
+
+ /* leave the lock */
+
+ finalizer_thread_mutex->unlock();
+
+#if !defined(NDEBUG)
+ if (opt_DebugFinalizer)
+ log_println("[finalizer thread : status=awake]");
+#endif
+
+ /* and call the finalizers */
+
+ gc_invoke_finalizers();
+
+#if !defined(NDEBUG)
+ if (opt_DebugFinalizer)
+ log_println("[finalizer thread : status=sleeping]");
+#endif
+ }
+}
+#endif
+
+
+/* finalizer_start_thread ******************************************************
+
+ Starts the finalizer thread.
+
+*******************************************************************************/
+
+#if defined(ENABLE_THREADS)
+bool finalizer_start_thread(void)
+{
+ utf *name;
+
+ name = utf_new_char("Finalizer");
+
+ if (!threads_thread_start_internal(name, finalizer_thread))
+ return false;
+
+ /* everything's ok */
+
+ return true;
+}
+#endif
+
+
+/* finalizer_notify ************************************************************
+
+ Notifies the finalizer thread that it should run the
+ gc_invoke_finalizers from the GC.
+
+*******************************************************************************/
+
+void finalizer_notify(void)
+{
+#if !defined(NDEBUG)
+ if (opt_DebugFinalizer)
+ log_println("[finalizer notified]");
+#endif
+
+#if defined(ENABLE_THREADS)
+ /* get the lock on the finalizer lock object, so we can call wait */
+
+ finalizer_thread_mutex->lock();
+
+ /* signal the finalizer thread */
+
+ finalizer_thread_cond->signal();
+
+ /* leave the lock */
+
+ finalizer_thread_mutex->unlock();
+#else
+ /* if we don't have threads, just run the finalizers */
+
+ gc_invoke_finalizers();
+#endif
+}
+
+
+/* finalizer_run ***************************************************************
+
+ Actually run the finalizer functions.
+
+*******************************************************************************/
+
+void finalizer_run(void *o, void *p)
+{
+ java_handle_t *h;
+ classinfo *c;
+
+ h = (java_handle_t *) o;
+
+#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
+ /* XXX this is only a dirty hack to make Boehm work with handles */
+
+ h = LLNI_WRAP((java_object_t *) h);
+#endif
+
+ LLNI_class_get(h, c);
+
+#if !defined(NDEBUG)
+ if (opt_DebugFinalizer) {
+ log_start();
+ log_print("[finalizer running : o=%p p=%p class=", o, p);
+ class_print(c);
+ log_print("]");
+ log_finish();
+ }
+#endif
+
+ /* call the finalizer function */
+
+ (void) vm_call_method(c->finalizer, h);
+
+#if !defined(NDEBUG)
+ if (opt_DebugFinalizer && (exceptions_get_exception() != NULL)) {
+ log_println("[finalizer exception]");
+ exceptions_print_stacktrace();
+ }
+#endif
+
+ /* if we had an exception in the finalizer, ignore it */
+
+ exceptions_clear_exception();
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
+++ /dev/null
-/* src/vm/finalizer.h - finalizer linked list and thread header
-
- Copyright (C) 1996-2005, 2006, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _FINALIZER_H
-#define _FINALIZER_H
-
-#include "config.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "vm/types.h"
-
-#include "vm/global.h"
-
-
-/* function prototypes ********************************************************/
-
-bool finalizer_init(void);
-bool finalizer_start_thread(void);
-void finalizer_notify(void);
-void finalizer_run(void *o, void *p);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _FINALIZER_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
--- /dev/null
+/* src/vm/finalizer.h - finalizer linked list and thread header
+
+ Copyright (C) 1996-2005, 2006, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _FINALIZER_H
+#define _FINALIZER_H
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "vm/types.h"
+
+#include "vm/global.h"
+
+
+/* function prototypes ********************************************************/
+
+bool finalizer_init(void);
+bool finalizer_start_thread(void);
+void finalizer_notify(void);
+void finalizer_run(void *o, void *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FINALIZER_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
#include "native/vm/reflection.hpp"
-#include "vm/access.h"
+#include "vm/access.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
#include "vm/field.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#ifdef __cplusplus
linenumbertable.hpp \
methodtree.c \
methodtree.h \
- parse.c \
- parse.h \
+ parse.cpp \
+ parse.hpp \
patcher-common.cpp \
patcher-common.hpp \
$(RECOMPILE_SOURCES) \
#include "vm/jit/builtin.hpp"
#include "vm/exceptions.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/resolve.hpp"
#include "vm/jit/codegen-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/exceptions.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/resolve.hpp"
#include "vm/string.hpp"
#include "vm/jit/codegen-common.hpp"
#include "vm/jit/jit.hpp"
-#include "vm/jit/inline/inline.h"
+#include "vm/jit/inline/inline.hpp"
/* function prototypes ********************************************************/
#include "vm/jit/emit-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher-common.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "vm/jit/alpha/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
#include "native/llni.h"
#include "vm/array.hpp"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/primitive.hpp"
#include "vm/resolve.hpp"
#include "vm/vm.hpp"
#include <stdint.h>
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* function prototypes ********************************************************/
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
#include "vm/jit/methodheader.h"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher-common.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/arm/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
#include "vm/global.h"
#include "vm/globals.hpp"
#include "vm/initialize.hpp"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/loader.hpp"
#include "vm/options.h"
#include "vm/primitive.hpp"
#include "toolbox/logging.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/utf8.h"
#include "toolbox/list.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/exceptiontable.h"
#include "vm/jit/linenumbertable.hpp"
#include "vm/jit/builtin.hpp"
#include "vm/exceptions.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/string.hpp"
#include "toolbox/list.hpp"
#include "vm/jit/builtin.hpp"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/references.h"
#include "vm/jit/dseg.h"
#if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER
-void emit_label_beqz(codegendata *cd, s4 label, s4 reg)
+void emit_label_beqz(codegendata* cd, int label, int reg)
{
emit_label_bccz(cd, label, BRANCH_EQ, reg, BRANCH_OPT_NONE);
}
+void emit_label_bnez(codegendata* cd, int label, int reg)
+{
+ emit_label_bccz(cd, label, BRANCH_NE, reg, BRANCH_OPT_NONE);
+}
+
+void emit_label_bltz(codegendata* cd, int label, int reg)
+{
+ emit_label_bccz(cd, label, BRANCH_LT, reg, BRANCH_OPT_NONE);
+}
+
+void emit_label_bgtz(codegendata* cd, int label, int reg)
+{
+ emit_label_bccz(cd, label, BRANCH_GT, reg, BRANCH_OPT_NONE);
+}
+
#endif /* SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER */
+/* emit_label_bxx **************************************************************
+
+ Wrappers for label-branches on two integer registers.
+
+ We use PACK_REGS here, so we don't have to change the branchref
+ data structure and the emit_bccz function.
+
+*******************************************************************************/
+
+#if SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS
+
+void emit_label_bne(codegendata* cd, int label, int s1, int s2)
+{
+ emit_label_bccz(cd, label, BRANCH_NE, PACK_REGS(s1, s2), BRANCH_OPT_NONE);
+}
+
+#endif /* SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS */
+
+
/* emit_label_bxx **************************************************************
Wrappers for label-branches on condition codes.
/* src/vm/jit/emit-common.hpp - common code emitter functions
- Copyright (C) 2006, 2007 R. Grafl, A. Krall, C. Kruegel, C. Oates,
- R. Obermaisser, M. Platter, M. Probst, S. Ring, E. Steiner,
- C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich, J. Wenninger,
- Institut f. Computersprachen - TU Wien
+ Copyright (C) 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
This file is part of CACAO.
void emit_label_br(codegendata *cd, s4 label);
#if SUPPORT_BRANCH_CONDITIONAL_ONE_INTEGER_REGISTER
-void emit_label_beqz(codegendata *cd, s4 label, s4 reg);
+void emit_label_beqz(codegendata* cd, int label, int reg);
+void emit_label_bnez(codegendata* cd, int label, int reg);
+void emit_label_bltz(codegendata* cd, int label, int reg);
+void emit_label_bgtz(codegendata* cd, int label, int reg);
+#endif
+
+#if SUPPORT_BRANCH_CONDITIONAL_TWO_INTEGER_REGISTERS
+void emit_label_bne(codegendata* cd, int label, int s1, int s2);
#endif
#if SUPPORT_BRANCH_CONDITIONAL_CONDITION_REGISTER
#include "md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/os.hpp"
#include "vm/jit/abi.h"
#include "vm/jit/emit-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher-common.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "vm/jit/i386/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
libinline.la
libinline_la_SOURCES = \
- inline.c \
- inline.h \
+ inline.cpp \
+ inline.hpp \
inline_debug.inc
+++ /dev/null
-/* src/vm/jit/inline/inline.c - method inlining
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "vm/types.h"
-
-#include "mm/memory.h"
-
-#include "threads/lock.hpp"
-#include "threads/mutex.hpp"
-#include "threads/thread.hpp"
-
-#include "toolbox/logging.h"
-
-#include "vm/jit/builtin.hpp"
-#include "vm/class.hpp"
-#include "vm/global.h"
-#include "vm/initialize.hpp"
-#include "vm/method.h"
-#include "vm/options.h"
-#include "vm/statistics.h"
-
-#include "vm/jit/jit.hpp"
-#include "vm/jit/parse.h"
-#include "vm/jit/reg.h"
-#include "vm/jit/show.hpp"
-#include "vm/jit/stack.h"
-
-#include "vm/jit/inline/inline.h"
-#include "vm/jit/loop/loop.h"
-
-#include "vm/jit/verify/typecheck.h"
-
-
-/* algorithm tuning constants *************************************************/
-
-/* Algorithm Selection */
-/* Define exactly one of the following three to select the inlining */
-/* heuristics. */
-
-/*#define INLINE_DEPTH_FIRST*/
-/*#define INLINE_BREADTH_FIRST*/
-#define INLINE_KNAPSACK
-
-/* Parameters for knapsack heuristics: */
-
-#if defined(INLINE_KNAPSACK)
-
-#define INLINE_COUNTDOWN_INIT 1000
-#define INLINE_COST_OFFSET -16
-#define INLINE_COST_BUDGET 100
-/*#define INLINE_WEIGHT_BUDGET 5.0*/
-/*#define INLINE_ADD_NEGATIVE_TO_BUDGET*/
-/*#define INLINE_MAX_DEPTH 3*/
-/*#define INLINE_DIVIDE_COST_BY_FREQ */
-
-#endif
-
-/* Parameters for depth-first heuristics: */
-
-#if defined(INLINE_DEPTH_FIRST)
-
-#define INLINE_MAX_DEPTH 3
-#define INLINE_MAX_BLOCK_EXPANSION 10
-/*#define INLINE_MAX_ICMD_EXPANSION 10*/
-/*#define INLINE_CANCEL_ON_THRESHOLD*/
-
-#endif
-
-/* Parameters for breadth-first heuristics: */
-
-#if defined(INLINE_BREADTH_FIRST)
-
-/*#define INLINE_MAX_BLOCK_EXPANSION 10*/
-#define INLINE_MAX_ICMD_EXPANSION 5
-
-#endif
-
-
-/* debugging ******************************************************************/
-
-#if !defined(NDEBUG)
-#define INLINE_VERBOSE
-#define DOLOG(code) do{ if (opt_TraceInlining >= 2) { code; } }while(0)
-#define DOLOG_SHORT(code) do{ if (opt_TraceInlining >= 1) { code; } }while(0)
-#else
-#define DOLOG(code)
-#endif
-
-#if defined(ENABLE_VERIFIER) && !defined(NDEBUG)
-/* Define this to verify the resulting code after inlining. */
-/* Note: This is only useful for development and may require patches to the */
-/* verifier code. */
-/* #define INLINE_VERIFY_RESULT */
-#endif
-
-
-/* types **********************************************************************/
-
-typedef struct inline_node inline_node;
-typedef struct inline_target_ref inline_target_ref;
-typedef struct inline_context inline_context;
-typedef struct inline_block_map inline_block_map;
-typedef struct inline_site inline_site;
-typedef struct inline_candidate inline_candidate;
-
-struct inline_node {
- inline_context *ctx;
-
- jitdata *jd;
- methodinfo *m;
- inline_node *children;
- inline_node *next; /* next node at this depth */
- inline_node *prev; /* prev node at this depth */
- int depth; /* inlining depth, 0 for root */
-
- /* info about the call site (if depth > 0)*/
- inline_node *parent; /* node of the caller (NULL for root) */
- basicblock *callerblock; /* original block containing the INVOKE* */
- instruction *callerins; /* the original INVOKE* instruction */
- s4 callerpc;
- s4 *n_passthroughvars;
- int n_passthroughcount;
- int n_selfpassthroughcount; /* # of pass-through vars of the call itself */
- exception_entry **o_handlers;
- int n_handlercount; /* # of handlers protecting this call */
- int n_resultlocal;
- int synclocal; /* variable used for synchr., or UNUSED */
- bool isstatic; /* this is a static call */
-
- bool blockbefore; /* block boundary before inlined body? */
- bool blockafter; /* block boundary after inlined body? */
-
- /* info about the callee */
- int localsoffset;
- int prolog_instructioncount; /* # of ICMDs in the inlining prolog */
- int epilog_instructioncount; /* # of ICMDs in the inlining epilog */
- int extra_instructioncount;
- int extra_exceptiontablelength; /* # of extra handlers to put in caller */
- bool synchronize; /* do we have to synchronize enter/exit? */
- basicblock *handler_monitorexit; /* handler for synchronized inlinees */
- s4 *varmap;
-
- /* cumulative values */
- int cumul_instructioncount; /* ICMDs in this node and its children */
- int cumul_basicblockcount; /* BBs started by this node and its children */
- int cumul_basicblockcount_root; /* BBs that have to be added to the root */
- /* node if this node is inlined */
- int cumul_blockmapcount;
- int cumul_maxlocals;
- int cumul_exceptiontablelength;
-
- /* output */
- instruction *inlined_iinstr;
- instruction *inlined_iinstr_cursor;
- basicblock *inlined_basicblocks;
- basicblock *inlined_basicblocks_cursor;
-
- /* register data */
- registerdata *regdata;
-
- /* temporary */
- inline_target_ref *refs;
- instruction *inline_start_instruction;
- s4 *javalocals;
-
- /* XXX debug */
- char *indent;
- int debugnr;
-};
-
-struct inline_target_ref {
- inline_target_ref *next;
- union {
- basicblock **block;
- s4 *nr;
- } ref;
- basicblock *target;
- bool isnumber;
-};
-
-struct inline_block_map {
- inline_node *iln;
- basicblock *o_block;
- basicblock *n_block;
-};
-
-struct inline_context {
- inline_node *master;
-
- jitdata *resultjd;
-
- inline_candidate *candidates;
-
- int next_block_number;
- inline_block_map *blockmap;
- int blockmap_index;
-
- int maxinoutdepth;
-
- bool stopped;
-
- int next_debugnr; /* XXX debug */
-};
-
-struct inline_site {
- bool speculative; /* true, if inlining would be speculative */
- bool inlined; /* true, if this site has been inlined */
-
- basicblock *bptr; /* basic block containing the call site */
- instruction *iptr; /* the invocation instruction */
- exception_entry **handlers; /* active handlers at the call site */
- s4 nhandlers; /* number of active handlers */
- s4 pc; /* PC of the invocation instruction */
-};
-
-struct inline_candidate {
- inline_candidate *next;
- int freq;
- int cost;
- double weight;
- inline_node *caller;
- methodinfo *callee;
- inline_site site;
-};
-
-
-/* prototypes *****************************************************************/
-
-static bool inline_analyse_code(inline_node *iln);
-static void inline_post_process(jitdata *jd);
-
-
-/* debug helpers **************************************************************/
-
-#if !defined(NDEBUG)
-#include "inline_debug.inc"
-#endif
-
-
-/* statistics *****************************************************************/
-
-/*#define INLINE_STATISTICS*/
-
-#if !defined(NDEBUG)
-#define INLINE_STATISTICS
-#endif
-
-#if defined(INLINE_STATISTICS)
-int inline_stat_roots = 0;
-int inline_stat_roots_transformed = 0;
-int inline_stat_inlined_nodes = 0;
-int inline_stat_max_depth = 0;
-
-void inline_print_stats()
-{
- printf("inlining statistics:\n");
- printf(" roots analysed : %d\n", inline_stat_roots);
- printf(" roots transformed: %d\n", inline_stat_roots_transformed);
- printf(" inlined nodes : %d\n", inline_stat_inlined_nodes);
- printf(" max depth : %d\n", inline_stat_max_depth);
-}
-#endif
-
-
-/* compilation of callees *****************************************************/
-
-static bool inline_jit_compile_intern(jitdata *jd)
-{
- methodinfo *m;
-
- /* XXX should share code with jit.c */
-
- assert(jd);
-
- /* XXX initialize the static function's class */
-
- m = jd->m;
-
- /* call the compiler passes ***********************************************/
-
- /* call parse pass */
-
- DOLOG( log_message_class("Parsing ", m->clazz) );
- if (!parse(jd)) {
- return false;
- }
-
- /* call stack analysis pass */
-
- if (!stack_analyse(jd)) {
- return false;
- }
-
- return true;
-}
-
-
-static bool inline_jit_compile(inline_node *iln)
-{
- bool r;
- methodinfo *m;
- jitdata *jd;
-
- /* XXX should share code with jit.c */
-
- assert(iln);
- m = iln->m;
- assert(m);
-
- /* enter a monitor on the method */
-
- Mutex_lock(m->mutex);
-
- /* allocate jitdata structure and fill it */
-
- jd = jit_jitdata_new(m);
- iln->jd = jd;
-
- jd->flags = 0; /* XXX */
-
- /* initialize the register allocator */
-
- reg_setup(jd);
-
- /* setup the codegendata memory */
-
- /* XXX do a pseudo setup */
- jd->cd = DNEW(codegendata);
- MZERO(jd->cd, codegendata, 1);
- jd->cd->method = m;
- /* XXX uses too much dump memory codegen_setup(jd); */
-
- /* now call internal compile function */
-
- r = inline_jit_compile_intern(jd);
-
- if (r) {
- iln->regdata = jd->rd;
- }
-
- /* free some memory */
-#if 0
-
-#if defined(ENABLE_JIT)
-# if defined(ENABLE_INTRP)
- if (!opt_intrp)
-# endif
- codegen_free(jd);
-#endif
-
-#endif
-
- /* leave the monitor */
-
- Mutex_unlock(m->mutex);
-
- return r;
-}
-
-
-/* inlining tree handling *****************************************************/
-
-static void inline_insert_inline_node(inline_node *parent, inline_node *child)
-{
- inline_node *first;
- inline_node *succ;
-
- assert(parent && child);
-
- child->parent = parent;
-
- child->debugnr = parent->ctx->next_debugnr++; /* XXX debug */
-
- first = parent->children;
- if (!first) {
- /* insert as only node */
- parent->children = child;
- child->next = child;
- child->prev = child;
- return;
- }
-
- /* {there is at least one child already there} */
-
- /* XXX is this search necessary, or could we always add at the end? */
-
- succ = first;
- while (succ->callerpc < child->callerpc) {
- succ = succ->next;
- if (succ == first) {
- /* insert as last node */
- child->prev = first->prev;
- child->next = first;
- child->prev->next = child;
- child->next->prev = child;
- return;
- }
- }
-
- assert(succ->callerpc > child->callerpc);
-
- /* insert before succ */
-
- child->prev = succ->prev;
- child->next = succ;
- child->prev->next = child;
- child->next->prev = child;
-
- if (parent->children == succ)
- parent->children = child;
-}
-
-
-static void inline_remove_inline_node(inline_node *parent, inline_node *child)
-{
- assert(parent);
- assert(child);
- assert(child->parent == parent);
-
- if (child->prev == child) {
- /* remove the only child node */
- parent->children = NULL;
- }
- else {
- child->prev->next = child->next;
- child->next->prev = child->prev;
-
- if (parent->children == child)
- parent->children = child->next;
- }
-}
-
-
-/* inlining candidate handling ************************************************/
-
-#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)
-static void inline_add_candidate(inline_context *ctx,
- inline_node *caller,
- methodinfo *callee,
- inline_site *site)
-{
- inline_candidate **link;
- inline_candidate *cand;
-
- cand = DNEW(inline_candidate);
-#if defined(INLINE_DIVIDE_COST_BY_FREQ)
- cand->freq = INLINE_COUNTDOWN_INIT - callee->hitcountdown;
- if (cand->freq < 1)
-#endif
- cand->freq = 1;
-#if defined(INLINE_KNAPSACK)
- cand->cost = callee->jcodelength + INLINE_COST_OFFSET;
-#endif
-#if defined(INLINE_BREADTH_FIRST)
- cand->cost = caller->depth;
-#endif
- cand->caller = caller;
- cand->callee = callee;
- cand->site = *site;
-
- cand->weight = (double)cand->cost / cand->freq;
-
- for (link = &(ctx->candidates); ; link = &((*link)->next)) {
- if (!*link || (*link)->weight > cand->weight) {
- cand->next = *link;
- *link = cand;
- break;
- }
- }
-}
-#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */
-
-#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)
-static inline_candidate * inline_pick_best_candidate(inline_context *ctx)
-{
- inline_candidate *cand;
-
- cand = ctx->candidates;
-
- if (cand)
- ctx->candidates = cand->next;
-
- return cand;
-}
-#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */
-
-#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST))
-static void inline_candidate_println(inline_candidate *cand)
-{
- printf("%10g (%5d / %5d) depth %2d ",
- cand->weight, cand->cost, cand->freq, cand->caller->depth + 1);
- method_println(cand->callee);
-}
-#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */
-
-
-#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST))
-static void inline_candidates_println(inline_context *ctx)
-{
- inline_candidate *cand;
-
- for (cand = ctx->candidates; cand != NULL; cand = cand->next) {
- printf(" ");
- inline_candidate_println(cand);
- }
-}
-#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */
-
-
-/* variable handling **********************************************************/
-
-static s4 inline_new_variable(jitdata *jd, s4 type, s4 flags)
-{
- s4 index;
- s4 newcount;
-
- index = jd->vartop++;
- if (index >= jd->varcount) {
- newcount = jd->vartop * 2; /* XXX */
- jd->var = DMREALLOC(jd->var, varinfo, jd->varcount, newcount);
- MZERO(jd->var + jd->varcount, varinfo, (newcount - jd->varcount));
- jd->varcount = newcount;
- }
-
- jd->var[index].type = type;
- jd->var[index].flags = flags;
-
- return index;
-}
-
-
-static s4 inline_new_variable_clone(jitdata *jd, jitdata *origjd, s4 origidx)
-{
- varinfo *v;
- s4 newidx;
-
- v = &(origjd->var[origidx]);
-
- newidx = inline_new_variable(jd, v->type, v->flags);
-
- jd->var[newidx].vv = v->vv;
-
- return newidx;
-}
-
-
-static s4 inline_new_temp_variable(jitdata *jd, s4 type)
-{
- return inline_new_variable(jd, type, 0);
-}
-
-
-static s4 inline_translate_variable(jitdata *jd, jitdata *origjd, s4 *varmap, s4 index)
-{
- s4 idx;
-
- idx = varmap[index];
-
- if (idx < 0) {
- idx = inline_new_variable_clone(jd, origjd, index);
- varmap[index] = idx;
- }
-
- return idx;
-}
-
-
-static s4 *create_variable_map(inline_node *callee)
-{
- s4 *varmap;
- s4 i, t;
- s4 varindex;
- s4 n_javaindex;
- s4 avail;
- varinfo *v;
-
- /* create the variable mapping */
-
- varmap = DMNEW(s4, callee->jd->varcount);
- for (i=0; i<callee->jd->varcount; ++i)
- varmap[i] = -1;
-
- /* translate local variables */
-
- for (i=0; i<callee->m->maxlocals; ++i) {
- for (t=0; t<5; ++t) {
- varindex = callee->jd->local_map[5*i + t];
- if (varindex == UNUSED)
- continue;
-
- v = &(callee->jd->var[varindex]);
- assert(v->type == t || v->type == TYPE_VOID); /* XXX stack leaves VOID */
- v->type = t; /* XXX restore if it is TYPE_VOID */
-
- avail = callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t];
-
- if (avail == UNUSED) {
- avail = inline_new_variable_clone(callee->ctx->resultjd, callee->jd, varindex);
- callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t] = avail;
- }
-
- varmap[varindex] = avail;
- }
- }
-
- /* for synchronized instance methods we need an extra local */
-
- if (callee->synchronize && !(callee->m->flags & ACC_STATIC)) {
- n_javaindex = callee->localsoffset - 1;
- assert(n_javaindex >= 0);
- assert(callee->parent);
- assert(n_javaindex == callee->parent->localsoffset + callee->parent->m->maxlocals);
-
- avail = callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR];
-
- if (avail == UNUSED) {
- avail = inline_new_variable(callee->ctx->resultjd, TYPE_ADR, 0);
- callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR] = avail;
- }
-
- callee->synclocal = avail;
- }
- else {
- callee->synclocal = UNUSED;
- }
-
- return varmap;
-}
-
-
-/* basic block translation ****************************************************/
-
-#define INLINE_RETURN_REFERENCE(callee) \
- ( (basicblock *) (ptrint) (0x333 + (callee)->depth) )
-
-
-static void inline_add_block_reference(inline_node *iln, basicblock **blockp)
-{
- inline_target_ref *ref;
-
- ref = DNEW(inline_target_ref);
- ref->ref.block = blockp;
- ref->isnumber = false;
- ref->next = iln->refs;
- iln->refs = ref;
-}
-
-
-#if 0
-static void inline_add_blocknr_reference(inline_node *iln, s4 *nrp)
-{
- inline_target_ref *ref;
-
- ref = DNEW(inline_target_ref);
- ref->ref.nr = nrp;
- ref->isnumber = true;
- ref->next = iln->refs;
- iln->refs = ref;
-}
-#endif
-
-
-static void inline_block_translation(inline_node *iln, basicblock *o_bptr, basicblock *n_bptr)
-{
- inline_context *ctx;
-
- ctx = iln->ctx;
- assert(ctx->blockmap_index < ctx->master->cumul_blockmapcount);
-
- ctx->blockmap[ctx->blockmap_index].iln = iln;
- ctx->blockmap[ctx->blockmap_index].o_block = o_bptr;
- ctx->blockmap[ctx->blockmap_index].n_block = n_bptr;
-
- ctx->blockmap_index++;
-}
-
-
-static basicblock * inline_map_block(inline_node *iln,
- basicblock *o_block,
- inline_node *targetiln)
-{
- inline_block_map *bm;
- inline_block_map *bmend;
-
- assert(iln);
- assert(targetiln);
-
- if (!o_block)
- return NULL;
-
- bm = iln->ctx->blockmap;
- bmend = bm + iln->ctx->blockmap_index;
-
- while (bm < bmend) {
- assert(bm->iln && bm->o_block && bm->n_block);
- if (bm->o_block == o_block && bm->iln == targetiln)
- return bm->n_block;
- bm++;
- }
-
- assert(false);
- return NULL; /* not reached */
-}
-
-
-static void inline_resolve_block_refs(inline_target_ref **refs,
- basicblock *o_bptr,
- basicblock *n_bptr,
- bool returnref)
-{
- inline_target_ref *ref;
- inline_target_ref *prev;
-
- prev = NULL;
- for (ref = *refs; ref != NULL; ref = ref->next) {
- if (ref->isnumber && !returnref) {
- if (*(ref->ref.nr) == JAVALOCAL_FROM_RETADDR(o_bptr->nr)) {
- *(ref->ref.nr) = JAVALOCAL_FROM_RETADDR(n_bptr->nr);
- goto remove_ref;
- }
- }
- else {
- if (*(ref->ref.block) == o_bptr) {
- *(ref->ref.block) = n_bptr;
- goto remove_ref;
- }
- }
-
- /* skip this ref */
-
- prev = ref;
- continue;
-
-remove_ref:
- /* remove this ref */
-
- if (prev) {
- prev->next = ref->next;
- }
- else {
- *refs = ref->next;
- }
- }
-}
-
-
-/* basic block creation *******************************************************/
-
-static basicblock * create_block(inline_node *container,
- inline_node *iln,
- inline_node *inner,
- int indepth)
-{
- basicblock *n_bptr;
- inline_node *outer;
- s4 i;
- s4 depth;
- s4 varidx;
- s4 newvaridx;
-
- assert(container);
- assert(iln);
- assert(inner);
- assert(indepth >= 0);
-
- n_bptr = container->inlined_basicblocks_cursor++;
- assert(n_bptr);
- assert((n_bptr - container->inlined_basicblocks) < container->cumul_basicblockcount);
-
- BASICBLOCK_INIT(n_bptr, iln->m);
-
- n_bptr->iinstr = container->inlined_iinstr_cursor;
- n_bptr->next = n_bptr + 1;
- n_bptr->nr = container->ctx->next_block_number++;
- n_bptr->indepth = indepth;
- n_bptr->flags = BBFINISHED; /* XXX */
-
- /* set the inlineinfo of the new block */
-
- if (iln->inline_start_instruction)
- n_bptr->inlineinfo = iln->inline_start_instruction->sx.s23.s3.inlineinfo;
-
- if (indepth > container->ctx->maxinoutdepth)
- container->ctx->maxinoutdepth = indepth;
-
- if (indepth) {
- n_bptr->invars = DMNEW(s4, indepth);
-
-
- for (i=0; i<indepth; ++i)
- n_bptr->invars[i] = -1; /* XXX debug */
-
- /* pass-through variables enter the block */
-
- outer = inner->parent;
- while (outer != NULL) {
- depth = outer->n_passthroughcount;
-
- assert(depth + inner->n_selfpassthroughcount <= indepth);
-
- for (i=0; i<inner->n_selfpassthroughcount; ++i) {
- varidx = inner->n_passthroughvars[i];
- newvaridx =
- inline_new_variable_clone(container->ctx->resultjd,
- outer->jd,
- varidx);
- n_bptr->invars[depth + i] = newvaridx;
- outer->varmap[varidx] = newvaridx;
- }
- inner = outer;
- outer = outer->parent;
- }
- }
- else {
- n_bptr->invars = NULL;
- }
-
- /* XXX for the verifier. should not be here */
-
- {
- varinfo *dv;
-
- dv = DMNEW(varinfo, iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS);
- MZERO(dv, varinfo, iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS);
- n_bptr->inlocals = dv;
- }
-
- return n_bptr;
-}
-
-
-static s4 *translate_javalocals(inline_node *iln, s4 *javalocals)
-{
- s4 *jl;
- s4 i, j;
-
- jl = DMNEW(s4, iln->jd->maxlocals);
-
- for (i=0; i<iln->jd->maxlocals; ++i) {
- j = javalocals[i];
- if (j > UNUSED)
- j = inline_translate_variable(iln->ctx->resultjd, iln->jd, iln->varmap, j);
- jl[i] = j;
-
-#if 0
- if (j < UNUSED) {
- /* an encoded returnAddress value - must be relocated */
- inline_add_blocknr_reference(iln, &(jl[i]));
- }
-#endif
- }
-
- return jl;
-}
-
-
-static basicblock * create_body_block(inline_node *iln,
- basicblock *o_bptr, s4 *varmap)
-{
- basicblock *n_bptr;
- s4 i;
-
- n_bptr = create_block(iln, iln, iln,
- o_bptr->indepth + iln->n_passthroughcount);
-
- n_bptr->type = o_bptr->type;
- n_bptr->flags = o_bptr->flags;
- n_bptr->bitflags = o_bptr->bitflags;
-
- /* resolve references to this block */
-
- inline_resolve_block_refs(&(iln->refs), o_bptr, n_bptr, false);
-
- /* translate the invars of the original block */
-
- for (i=0; i<o_bptr->indepth; ++i) {
- n_bptr->invars[iln->n_passthroughcount + i] =
- inline_translate_variable(iln->ctx->resultjd, iln->jd,
- varmap,
- o_bptr->invars[i]);
- }
-
- /* translate javalocals info (not for dead code) */
-
- if (n_bptr->flags >= BBREACHED)
- n_bptr->javalocals = translate_javalocals(iln, o_bptr->javalocals);
-
- return n_bptr;
-}
-
-
-static basicblock * create_epilog_block(inline_node *caller, inline_node *callee, s4 *varmap)
-{
- basicblock *n_bptr;
- s4 retcount;
- s4 idx;
-
- /* number of return variables */
-
- retcount = (callee->n_resultlocal == -1
- && callee->m->parseddesc->returntype.type != TYPE_VOID) ? 1 : 0;
-
- /* start the epilog block */
-
- n_bptr = create_block(caller, caller, callee,
- callee->n_passthroughcount + retcount);
-
- /* resolve references to the return block */
-
- inline_resolve_block_refs(&(callee->refs),
- INLINE_RETURN_REFERENCE(callee),
- n_bptr,
- true);
-
- /* return variable */
-
- if (retcount) {
- idx = inline_new_variable(caller->ctx->resultjd,
- callee->m->parseddesc->returntype.type, 0 /* XXX */);
- n_bptr->invars[callee->n_passthroughcount] = idx;
- varmap[callee->callerins->dst.varindex] = idx;
- }
-
- /* set javalocals */
-
- n_bptr->javalocals = DMNEW(s4, caller->jd->maxlocals);
- MCOPY(n_bptr->javalocals, caller->javalocals, s4, caller->jd->maxlocals);
-
- /* set block flags & type */
-
- n_bptr->flags = /* XXX original block flags */ BBFINISHED;
- n_bptr->type = BBTYPE_STD;
-
- return n_bptr;
-}
-
-
-static void close_block(inline_node *iln, inline_node *inner, basicblock *n_bptr, s4 outdepth)
-{
- inline_node *outer;
- s4 i;
- s4 depth;
- s4 varidx;
-
- n_bptr->outdepth = outdepth;
- n_bptr->outvars = DMNEW(s4, outdepth);
-
- for (i=0; i<outdepth; ++i)
- n_bptr->outvars[i] = 0; /* XXX debug */
-
- if (outdepth > iln->ctx->maxinoutdepth)
- iln->ctx->maxinoutdepth = outdepth;
-
- /* pass-through variables leave the block */
-
- outer = inner->parent;
- while (outer != NULL) {
- depth = outer->n_passthroughcount;
-
- assert(depth + inner->n_selfpassthroughcount <= outdepth);
-
- for (i=0; i<inner->n_selfpassthroughcount; ++i) {
- varidx = inner->n_passthroughvars[i];
- n_bptr->outvars[depth + i] =
- inline_translate_variable(iln->ctx->resultjd,
- outer->jd,
- outer->varmap,
- varidx);
- }
- inner = outer;
- outer = outer->parent;
- }
-}
-
-
-static void close_prolog_block(inline_node *iln,
- basicblock *n_bptr,
- inline_node *nextcall)
-{
- /* XXX add original outvars! */
- close_block(iln, nextcall, n_bptr, nextcall->n_passthroughcount);
-
- /* pass-through variables */
-
- DOLOG( printf("closed prolog block:\n");
- show_basicblock(iln->ctx->resultjd, n_bptr, SHOW_STACK); );
-}
-
-
-static void close_body_block(inline_node *iln,
- basicblock *n_bptr,
- basicblock *o_bptr,
- s4 *varmap,
- s4 retcount,
- s4 retidx)
-{
- s4 i;
-
- close_block(iln, iln, n_bptr, iln->n_passthroughcount + o_bptr->outdepth + retcount);
-
- /* translate the outvars of the original block */
-
- /* XXX reuse code */
- for (i=0; i<o_bptr->outdepth; ++i) {
- n_bptr->outvars[iln->n_passthroughcount + i] =
- inline_translate_variable(iln->ctx->resultjd, iln->jd, varmap,
- o_bptr->outvars[i]);
- }
-
- /* set the return variable, if any */
-
- if (retcount) {
- assert(retidx >= 0);
- n_bptr->outvars[iln->n_passthroughcount + o_bptr->outdepth] = retidx;
- }
-}
-
-
-/* inlined code generation ****************************************************/
-
-static instruction * inline_instruction(inline_node *iln,
- s4 opcode,
- instruction *o_iptr)
-{
- instruction *n_iptr;
-
- n_iptr = (iln->inlined_iinstr_cursor++);
- assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
-
- n_iptr->opc = opcode;
- n_iptr->flags.bits = o_iptr->flags.bits & INS_FLAG_ID_MASK;
- n_iptr->line = o_iptr->line;
-
- return n_iptr;
-}
-
-static void inline_generate_sync_builtin(inline_node *iln,
- inline_node *callee,
- instruction *o_iptr,
- s4 instancevar,
- functionptr func)
-{
- int syncvar;
- instruction *n_ins;
-
- if (callee->m->flags & ACC_STATIC) {
- /* ACONST */
- syncvar = inline_new_temp_variable(iln->ctx->resultjd, TYPE_ADR);
-
- n_ins = inline_instruction(iln, ICMD_ACONST, o_iptr);
- n_ins->sx.val.c.cls = callee->m->clazz;
- n_ins->dst.varindex = syncvar;
- n_ins->flags.bits |= INS_FLAG_CLASS;
- }
- else {
- syncvar = instancevar;
- }
-
- assert(syncvar != UNUSED);
-
- /* MONITORENTER / MONITOREXIT */
-
- n_ins = inline_instruction(iln, ICMD_BUILTIN, o_iptr);
- n_ins->sx.s23.s3.bte = builtintable_get_internal(func);
- n_ins->s1.argcount = 1; /* XXX add through-vars */
- n_ins->sx.s23.s2.args = DMNEW(s4, 1);
- n_ins->sx.s23.s2.args[0] = syncvar;
-}
-
-static s4 emit_inlining_prolog(inline_node *iln,
- inline_node *callee,
- instruction *o_iptr,
- s4 *varmap)
-{
- methodinfo *calleem;
- methoddesc *md;
- int i;
- int localindex;
- int type;
- instruction *n_ins;
- insinfo_inline *insinfo;
- s4 varindex;
-
- assert(iln && callee && o_iptr);
-
- calleem = callee->m;
- md = calleem->parseddesc;
-
- /* INLINE_START instruction */
-
- n_ins = inline_instruction(iln, ICMD_INLINE_START, o_iptr);
-
- insinfo = DNEW(insinfo_inline);
- insinfo->method = callee->m;
- insinfo->outer = iln->m;
- insinfo->synclocal = callee->synclocal;
- insinfo->synchronize = callee->synchronize;
- insinfo->javalocals_start = NULL;
- insinfo->javalocals_end = NULL;
-
- /* info about stack vars live at the INLINE_START */
-
- insinfo->throughcount = callee->n_passthroughcount;
- insinfo->paramcount = md->paramcount;
- insinfo->stackvarscount = o_iptr->s1.argcount;
- insinfo->stackvars = DMNEW(s4, insinfo->stackvarscount);
- for (i=0; i<insinfo->stackvarscount; ++i)
- insinfo->stackvars[i] = iln->varmap[o_iptr->sx.s23.s2.args[i]];
-
- /* info about the surrounding inlining */
-
- if (iln->inline_start_instruction)
- insinfo->parent = iln->inline_start_instruction->sx.s23.s3.inlineinfo;
- else
- insinfo->parent = NULL;
-
- /* finish the INLINE_START instruction */
-
- n_ins->sx.s23.s3.inlineinfo = insinfo;
- callee->inline_start_instruction = n_ins;
-
- DOLOG( printf("%sprolog: ", iln->indent);
- show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); );
-
- /* handle parameters for the inlined callee */
-
- localindex = callee->localsoffset + md->paramslots;
-
- for (i=md->paramcount-1; i>=0; --i) {
- assert(iln);
-
- type = md->paramtypes[i].type;
-
- localindex -= IS_2_WORD_TYPE(type) ? 2 : 1;
- assert(callee->regdata);
-
- /* translate the argument variable */
-
- varindex = varmap[o_iptr->sx.s23.s2.args[i]];
- assert(varindex != UNUSED);
-
- /* remove preallocation from the argument variable */
-
- iln->ctx->resultjd->var[varindex].flags &= ~(PREALLOC | INMEMORY);
-
- /* check the instance slot against NULL */
- /* we don't need that for <init> methods, as the verifier */
- /* ensures that they are only called for an uninit. object */
- /* (which may not be NULL). */
-
- if (!callee->isstatic && i == 0 && calleem->name != utf_init) {
- assert(type == TYPE_ADR);
- n_ins = inline_instruction(iln, ICMD_CHECKNULL, o_iptr);
- n_ins->s1.varindex = varindex;
- n_ins->dst.varindex = n_ins->s1.varindex;
- }
-
- /* store argument into local variable of inlined callee */
-
- if (callee->jd->local_map[5*(localindex - callee->localsoffset) + type] != UNUSED)
- {
- /* this value is used in the callee */
-
- if (i == 0 && callee->synclocal != UNUSED) {
- /* we also need it for synchronization, so copy it */
- assert(type == TYPE_ADR);
- n_ins = inline_instruction(iln, ICMD_COPY, o_iptr);
- }
- else {
- n_ins = inline_instruction(iln, ICMD_ISTORE + type, o_iptr);
- n_ins->sx.s23.s3.javaindex = UNUSED;
- }
- n_ins->s1.varindex = varindex;
- n_ins->dst.varindex = iln->ctx->resultjd->local_map[5*localindex + type];
- assert(n_ins->dst.varindex != UNUSED);
- }
- else if (i == 0 && callee->synclocal != UNUSED) {
- /* the value is not used inside the callee, but we need it for */
- /* synchronization */
- /* XXX In this case it actually makes no sense to create a */
- /* separate synchronization variable. */
-
- n_ins = inline_instruction(iln, ICMD_NOP, o_iptr);
- }
- else {
- /* this value is not used, pop it */
-
- n_ins = inline_instruction(iln, ICMD_POP, o_iptr);
- n_ins->s1.varindex = varindex;
- }
-
- DOLOG( printf("%sprolog: ", iln->indent);
- show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); );
- }
-
- /* COPY for synchronized instance methods */
-
- if (callee->synclocal != UNUSED) {
- n_ins = inline_instruction(iln, ICMD_COPY, o_iptr);
- n_ins->s1.varindex = varmap[o_iptr->sx.s23.s2.args[0]];
- n_ins->dst.varindex = callee->synclocal;
-
- assert(n_ins->s1.varindex != UNUSED);
- }
-
- if (callee->synchronize) {
- inline_generate_sync_builtin(iln, callee, o_iptr,
- (callee->isstatic) ? UNUSED : varmap[o_iptr->sx.s23.s2.args[0]],
- LOCK_monitor_enter);
- }
-
- /* INLINE_BODY instruction */
-
- n_ins = inline_instruction(iln, ICMD_INLINE_BODY, callee->jd->basicblocks[0].iinstr);
- n_ins->sx.s23.s3.inlineinfo = insinfo;
-
- return 0; /* XXX */
-}
-
-
-static void emit_inlining_epilog(inline_node *iln, inline_node *callee, instruction *o_iptr)
-{
- instruction *n_ins;
- s4 *jl;
-
- assert(iln && callee && o_iptr);
- assert(callee->inline_start_instruction);
-
- if (callee->synchronize) {
- inline_generate_sync_builtin(iln, callee, o_iptr,
- callee->synclocal,
- LOCK_monitor_exit);
- }
-
- /* INLINE_END instruction */
-
- n_ins = inline_instruction(iln, ICMD_INLINE_END, o_iptr);
- n_ins->sx.s23.s3.inlineinfo = callee->inline_start_instruction->sx.s23.s3.inlineinfo;
-
- /* set the javalocals */
-
- jl = DMNEW(s4, iln->jd->maxlocals);
- MCOPY(jl, iln->javalocals, s4, iln->jd->maxlocals);
- n_ins->sx.s23.s3.inlineinfo->javalocals_end = jl;
-
- DOLOG( printf("%sepilog: ", iln->indent);
- show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); );
-}
-
-
-#define TRANSLATE_VAROP(vo) \
- n_iptr->vo.varindex = inline_translate_variable(jd, origjd, varmap, n_iptr->vo.varindex)
-
-
-static void inline_clone_instruction(inline_node *iln,
- jitdata *jd,
- jitdata *origjd,
- s4 *varmap,
- instruction *o_iptr,
- instruction *n_iptr)
-{
- icmdtable_entry_t *icmdt;
- builtintable_entry *bte;
- methoddesc *md;
- s4 i, j;
- branch_target_t *table;
- lookup_target_t *lookup;
- inline_node *scope;
-
- *n_iptr = *o_iptr;
-
- icmdt = &(icmd_table[o_iptr->opc]);
-
- switch (icmdt->dataflow) {
- case DF_0_TO_0:
- break;
-
- case DF_3_TO_0:
- TRANSLATE_VAROP(sx.s23.s3);
- case DF_2_TO_0:
- TRANSLATE_VAROP(sx.s23.s2);
- case DF_1_TO_0:
- TRANSLATE_VAROP(s1);
- break;
-
- case DF_2_TO_1:
- TRANSLATE_VAROP(sx.s23.s2);
- case DF_1_TO_1:
- case DF_COPY:
- case DF_MOVE:
- TRANSLATE_VAROP(s1);
- case DF_0_TO_1:
- TRANSLATE_VAROP(dst);
- break;
-
- case DF_N_TO_1:
- n_iptr->sx.s23.s2.args = DMNEW(s4, n_iptr->s1.argcount);
- for (i=0; i<n_iptr->s1.argcount; ++i) {
- n_iptr->sx.s23.s2.args[i] =
- inline_translate_variable(jd, origjd, varmap,
- o_iptr->sx.s23.s2.args[i]);
- }
- TRANSLATE_VAROP(dst);
- break;
-
- case DF_INVOKE:
- INSTRUCTION_GET_METHODDESC(n_iptr, md);
-clone_call:
- n_iptr->s1.argcount += iln->n_passthroughcount;
- n_iptr->sx.s23.s2.args = DMNEW(s4, n_iptr->s1.argcount);
- for (i=0; i<o_iptr->s1.argcount; ++i) {
- n_iptr->sx.s23.s2.args[i] =
- inline_translate_variable(jd, origjd, varmap,
- o_iptr->sx.s23.s2.args[i]);
- }
- for (scope = iln; scope != NULL; scope = scope->parent) {
- for (j = 0; j < scope->n_selfpassthroughcount; ++j) {
- n_iptr->sx.s23.s2.args[i++] =
- scope->parent->varmap[scope->n_passthroughvars[j]];
- }
- }
- if (md->returntype.type != TYPE_VOID)
- TRANSLATE_VAROP(dst);
- break;
-
- case DF_BUILTIN:
- bte = n_iptr->sx.s23.s3.bte;
- md = bte->md;
- goto clone_call;
-
- default:
- assert(0);
- }
-
- switch (icmdt->controlflow) {
- case CF_RET:
- TRANSLATE_VAROP(s1); /* XXX should be handled by data-flow */
- /* FALLTHROUGH */
- case CF_IF:
- case CF_GOTO:
- inline_add_block_reference(iln, &(n_iptr->dst.block));
- break;
-
- case CF_JSR:
- inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.jsrtarget.block));
- break;
-
- case CF_TABLE:
- i = n_iptr->sx.s23.s3.tablehigh - n_iptr->sx.s23.s2.tablelow + 1 + 1 /* default */;
-
- table = DMNEW(branch_target_t, i);
- MCOPY(table, o_iptr->dst.table, branch_target_t, i);
- n_iptr->dst.table = table;
-
- while (--i >= 0) {
- inline_add_block_reference(iln, &(table->block));
- table++;
- }
- break;
-
- case CF_LOOKUP:
- inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.lookupdefault.block));
-
- i = n_iptr->sx.s23.s2.lookupcount;
- lookup = DMNEW(lookup_target_t, i);
- MCOPY(lookup, o_iptr->dst.lookup, lookup_target_t, i);
- n_iptr->dst.lookup = lookup;
-
- while (--i >= 0) {
- inline_add_block_reference(iln, &(lookup->target.block));
- lookup++;
- }
- break;
- }
-
- /* XXX move this to dataflow section? */
-
- switch (n_iptr->opc) {
- case ICMD_ASTORE:
-#if 0
- if (n_iptr->flags.bits & INS_FLAG_RETADDR)
- inline_add_blocknr_reference(iln, &(n_iptr->sx.s23.s2.retaddrnr));
-#endif
- /* FALLTHROUGH! */
- case ICMD_ISTORE:
- case ICMD_LSTORE:
- case ICMD_FSTORE:
- case ICMD_DSTORE:
- stack_javalocals_store(n_iptr, iln->javalocals);
- break;
- }
-}
-
-
-static void inline_rewrite_method(inline_node *iln)
-{
- basicblock *o_bptr;
- s4 len;
- instruction *o_iptr;
- instruction *n_iptr;
- inline_node *nextcall;
- basicblock *n_bptr;
- inline_block_map *bm;
- int i;
- int icount;
- jitdata *resultjd;
- jitdata *origjd;
- char indent[100]; /* XXX debug */
- s4 retcount;
- s4 retidx;
-
- assert(iln);
-
- resultjd = iln->ctx->resultjd;
- origjd = iln->jd;
-
- n_bptr = NULL;
- nextcall = iln->children;
-
- /* XXX debug */
- for (i=0; i<iln->depth; ++i)
- indent[i] = '\t';
- indent[i] = 0;
- iln->indent = indent;
-
- DOLOG( printf("%srewriting: ", indent); method_println(iln->m);
- printf("%s(passthrough: %d+%d)\n",
- indent, iln->n_passthroughcount - iln->n_selfpassthroughcount,
- iln->n_passthroughcount); );
-
- /* set memory cursors */
-
- iln->inlined_iinstr_cursor = iln->inlined_iinstr;
- iln->inlined_basicblocks_cursor = iln->inlined_basicblocks;
-
- /* allocate temporary buffers */
-
- iln->javalocals = DMNEW(s4, iln->jd->maxlocals);
-
- /* loop over basic blocks */
-
- o_bptr = iln->jd->basicblocks;
- for (; o_bptr; o_bptr = o_bptr->next) {
-
- if (o_bptr->flags < BBREACHED) {
-
- /* ignore the dummy end block */
-
- if (o_bptr->icount == 0 && o_bptr->next == NULL) {
- /* enter the following block as translation, for exception handler ranges */
- inline_block_translation(iln, o_bptr, iln->inlined_basicblocks_cursor);
- continue;
- }
-
- DOLOG(
- printf("%s* skipping old L%03d (flags=%d, type=%d, oid=%d) of ",
- indent,
- o_bptr->nr, o_bptr->flags, o_bptr->type,
- o_bptr->indepth);
- method_println(iln->m);
- );
-
- n_bptr = create_body_block(iln, o_bptr, iln->varmap);
-
- /* enter it in the blockmap */
-
- inline_block_translation(iln, o_bptr, n_bptr);
-
- close_body_block(iln, n_bptr, o_bptr, iln->varmap, 0, -1);
- continue;
- }
-
- len = o_bptr->icount;
- o_iptr = o_bptr->iinstr;
-
- DOLOG(
- printf("%s* rewriting old L%03d (flags=%d, type=%d, oid=%d) of ",
- indent,
- o_bptr->nr, o_bptr->flags, o_bptr->type,
- o_bptr->indepth);
- method_println(iln->m);
- show_basicblock(iln->jd, o_bptr, SHOW_STACK);
- );
-
- if (iln->blockbefore || o_bptr != iln->jd->basicblocks) {
- /* create an inlined clone of this block */
-
- n_bptr = create_body_block(iln, o_bptr, iln->varmap);
- icount = 0;
-
- /* enter it in the blockmap */
-
- inline_block_translation(iln, o_bptr, n_bptr);
-
- /* initialize the javalocals */
-
- MCOPY(iln->javalocals, n_bptr->javalocals, s4, iln->jd->maxlocals);
- }
- else {
- s4 *jl;
-
- /* continue caller block */
-
- n_bptr = iln->inlined_basicblocks_cursor - 1;
- icount = n_bptr->icount;
-
- /* translate the javalocals */
-
- jl = translate_javalocals(iln, o_bptr->javalocals);
- iln->inline_start_instruction->sx.s23.s3.inlineinfo->javalocals_start = jl;
-
- MCOPY(iln->javalocals, jl, s4, iln->jd->maxlocals);
- }
-
- /* iterate over the ICMDs of this block */
-
- retcount = 0;
- retidx = UNUSED;
-
- while (--len >= 0) {
-
- DOLOG( fputs(indent, stdout); show_icmd(iln->jd, o_iptr, false, SHOW_STACK);
- printf("\n") );
-
- /* handle calls that will be inlined */
-
- if (nextcall && o_iptr == nextcall->callerins) {
-
- /* write the inlining prolog */
-
- (void) emit_inlining_prolog(iln, nextcall, o_iptr, iln->varmap);
- icount += nextcall->prolog_instructioncount;
-
- /* end current block, or glue blocks together */
-
- n_bptr->icount = icount;
-
- if (nextcall->blockbefore) {
- close_prolog_block(iln, n_bptr, nextcall);
- }
- else {
- /* XXX */
- }
-
- /* check if the result is a local variable */
-
- if (nextcall->m->parseddesc->returntype.type != TYPE_VOID
- && o_iptr->dst.varindex < iln->jd->localcount)
- {
- nextcall->n_resultlocal = iln->varmap[o_iptr->dst.varindex];
- }
- else
- nextcall->n_resultlocal = -1;
-
- /* set memory pointers in the callee */
-
- nextcall->inlined_iinstr = iln->inlined_iinstr_cursor;
- nextcall->inlined_basicblocks = iln->inlined_basicblocks_cursor;
-
- /* recurse */
-
- DOLOG( printf("%sentering inline ", indent);
- show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") );
-
- inline_rewrite_method(nextcall);
-
- DOLOG( printf("%sleaving inline ", indent);
- show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") );
-
- /* update memory cursors */
-
- assert(nextcall->inlined_iinstr_cursor
- <= iln->inlined_iinstr_cursor + nextcall->cumul_instructioncount);
- assert(nextcall->inlined_basicblocks_cursor
- == iln->inlined_basicblocks_cursor + nextcall->cumul_basicblockcount);
- iln->inlined_iinstr_cursor = nextcall->inlined_iinstr_cursor;
- iln->inlined_basicblocks_cursor = nextcall->inlined_basicblocks_cursor;
-
- /* start new block, or glue blocks together */
-
- if (nextcall->blockafter) {
- n_bptr = create_epilog_block(iln, nextcall, iln->varmap);
- icount = 0;
- }
- else {
- n_bptr = iln->inlined_basicblocks_cursor - 1;
- icount = n_bptr->icount;
- /* XXX */
- }
-
- /* emit inlining epilog */
-
- emit_inlining_epilog(iln, nextcall, o_iptr);
- icount += nextcall->epilog_instructioncount;
-
- /* proceed to next call */
-
- nextcall = nextcall->next;
- }
- else {
- n_iptr = (iln->inlined_iinstr_cursor++);
- assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
-
- switch (o_iptr->opc) {
- case ICMD_RETURN:
- if (iln->depth == 0)
- goto default_clone;
- goto return_tail;
-
- case ICMD_IRETURN:
- case ICMD_ARETURN:
- case ICMD_LRETURN:
- case ICMD_FRETURN:
- case ICMD_DRETURN:
- if (iln->depth == 0)
- goto default_clone;
- retcount = 1;
- retidx = iln->varmap[o_iptr->s1.varindex];
- if (iln->n_resultlocal != -1) {
- /* store result in a local variable */
-
- DOLOG( printf("USING RESULTLOCAL %d\n", iln->n_resultlocal); );
- /* This relies on the same sequence of types for */
- /* ?STORE and ?RETURN opcodes. */
- n_iptr->opc = ICMD_ISTORE + (o_iptr->opc - ICMD_IRETURN);
- n_iptr->s1.varindex = retidx;
- n_iptr->dst.varindex = iln->n_resultlocal;
- n_iptr->sx.s23.s3.javaindex = UNUSED; /* XXX set real javaindex? */
-
- retcount = 0;
- retidx = UNUSED;
-
- n_iptr = (iln->inlined_iinstr_cursor++);
- assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
- icount++;
- }
- else if ((retidx < resultjd->localcount && iln->blockafter)
- || !iln->blockafter) /* XXX do we really always need the MOVE? */
- {
- /* local must not become outvar, insert a MOVE */
-
- n_iptr->opc = ICMD_MOVE;
- n_iptr->s1.varindex = retidx;
- retidx = inline_new_temp_variable(resultjd,
- resultjd->var[retidx].type);
- n_iptr->dst.varindex = retidx;
-
- n_iptr = (iln->inlined_iinstr_cursor++);
- assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
- icount++;
- }
-return_tail:
- if (iln->blockafter) {
- n_iptr->opc = ICMD_GOTO;
- n_iptr->dst.block = INLINE_RETURN_REFERENCE(iln);
- inline_add_block_reference(iln, &(n_iptr->dst.block));
- }
- else {
- n_iptr->opc = ICMD_NOP;
- }
- break;
-#if 0
- if (o_bptr->next == NULL || (o_bptr->next->icount==0 && o_bptr->next->next == NULL)) {
- n_iptr->opc = ICMD_NOP;
- break;
- }
- goto default_clone;
- break;
-#endif
-
- default:
-default_clone:
- inline_clone_instruction(iln, resultjd, iln->jd, iln->varmap, o_iptr, n_iptr);
- }
-
- DOLOG( fputs(indent, stdout); show_icmd(resultjd, n_iptr, false, SHOW_STACK);
- printf("\n"););
-
- icount++;
- }
-
- o_iptr++;
- }
-
- /* end of basic block */
-
- if (iln->blockafter || (o_bptr->next && o_bptr->next->next)) {
- close_body_block(iln, n_bptr, o_bptr, iln->varmap, retcount, retidx);
- n_bptr->icount = icount;
-
- DOLOG( printf("closed body block:\n");
- show_basicblock(resultjd, n_bptr, SHOW_STACK); );
- }
- else {
- n_bptr->icount = icount;
- assert(iln->parent);
- if (retidx != UNUSED)
- iln->parent->varmap[iln->callerins->dst.varindex] = retidx;
- }
- }
-
- bm = iln->ctx->blockmap;
- for (i=0; i<iln->ctx->blockmap_index; ++i, ++bm) {
- assert(bm->iln && bm->o_block && bm->n_block);
- if (bm->iln == iln)
- inline_resolve_block_refs(&(iln->refs), bm->o_block, bm->n_block, false);
- }
-
-#if !defined(NDEBUG)
- if (iln->refs) {
- inline_target_ref *ref;
- ref = iln->refs;
- while (ref) {
- if (!iln->depth || ref->isnumber || *(ref->ref.block) != INLINE_RETURN_REFERENCE(iln)) {
- DOLOG( printf("XXX REMAINING REF at depth %d: %p\n", iln->depth,
- (void*)*(ref->ref.block)) );
- assert(false);
- }
- ref = ref->next;
- }
- }
-#endif
-}
-
-
-static exception_entry * inline_exception_tables(inline_node *iln,
- exception_entry *n_extable,
- exception_entry **prevextable)
-{
- inline_node *child;
- inline_node *scope;
- exception_entry *et;
-
- assert(iln);
- assert(n_extable);
- assert(prevextable);
-
- child = iln->children;
- if (child) {
- do {
- n_extable = inline_exception_tables(child, n_extable, prevextable);
- child = child->next;
- } while (child != iln->children);
- }
-
- et = iln->jd->exceptiontable;
- for (; et != NULL; et = et->down) {
- assert(et);
- MZERO(n_extable, exception_entry, 1);
- n_extable->start = inline_map_block(iln, et->start , iln);
- n_extable->end = inline_map_block(iln, et->end , iln);
- n_extable->handler = inline_map_block(iln, et->handler, iln);
- n_extable->catchtype = et->catchtype;
-
- if (*prevextable) {
- (*prevextable)->down = n_extable;
- }
- *prevextable = n_extable;
-
- n_extable++;
- }
-
- if (iln->handler_monitorexit) {
- exception_entry **activehandlers;
-
- MZERO(n_extable, exception_entry, 1);
- n_extable->start = iln->inlined_basicblocks;
- n_extable->end = iln->inlined_basicblocks_cursor;
- n_extable->handler = iln->handler_monitorexit;
- n_extable->catchtype.any = NULL; /* finally */
-
- if (*prevextable) {
- (*prevextable)->down = n_extable;
- }
- *prevextable = n_extable;
-
- n_extable++;
-
- /* We have to protect the created handler with the same handlers */
- /* that protect the method body itself. */
-
- for (scope = iln; scope->parent != NULL; scope = scope->parent) {
-
- activehandlers = scope->o_handlers;
- assert(activehandlers);
-
- while (*activehandlers) {
-
- assert(scope->parent);
-
- MZERO(n_extable, exception_entry, 1);
- n_extable->start = iln->handler_monitorexit;
- n_extable->end = iln->handler_monitorexit + 1; /* XXX ok in this case? */
- n_extable->handler = inline_map_block(scope->parent,
- (*activehandlers)->handler,
- scope->parent);
- n_extable->catchtype = (*activehandlers)->catchtype;
-
- if (*prevextable) {
- (*prevextable)->down = n_extable;
- }
- *prevextable = n_extable;
-
- n_extable++;
- activehandlers++;
- }
- }
- }
-
- return n_extable;
-}
-
-
-static void inline_locals(inline_node *iln)
-{
- inline_node *child;
-
- assert(iln);
-
- iln->varmap = create_variable_map(iln);
-
- child = iln->children;
- if (child) {
- do {
- inline_locals(child);
- child = child->next;
- } while (child != iln->children);
- }
-
- if (iln->regdata->memuse > iln->ctx->resultjd->rd->memuse)
- iln->ctx->resultjd->rd->memuse = iln->regdata->memuse;
- if (iln->regdata->argintreguse > iln->ctx->resultjd->rd->argintreguse)
- iln->ctx->resultjd->rd->argintreguse = iln->regdata->argintreguse;
- if (iln->regdata->argfltreguse > iln->ctx->resultjd->rd->argfltreguse)
- iln->ctx->resultjd->rd->argfltreguse = iln->regdata->argfltreguse;
-}
-
-
-static void inline_interface_variables(inline_node *iln)
-{
- basicblock *bptr;
- jitdata *resultjd;
- s4 i;
- varinfo *v;
-
- resultjd = iln->ctx->resultjd;
-
- resultjd->interface_map = DMNEW(interface_info, 5*iln->ctx->maxinoutdepth);
- for (i=0; i<5*iln->ctx->maxinoutdepth; ++i)
- resultjd->interface_map[i].flags = UNUSED;
-
- for (bptr = resultjd->basicblocks; bptr != NULL; bptr = bptr->next) {
- assert(bptr->indepth <= iln->ctx->maxinoutdepth);
- assert(bptr->outdepth <= iln->ctx->maxinoutdepth);
-
- for (i=0; i<bptr->indepth; ++i) {
- v = &(resultjd->var[bptr->invars[i]]);
- v->flags |= INOUT;
- if (v->type == TYPE_RET)
- v->flags |= PREALLOC;
- else
- v->flags &= ~PREALLOC;
- v->flags &= ~INMEMORY;
- assert(bptr->invars[i] >= resultjd->localcount);
-
- if (resultjd->interface_map[5*i + v->type].flags == UNUSED) {
- resultjd->interface_map[5*i + v->type].flags = v->flags;
- }
- else {
- resultjd->interface_map[5*i + v->type].flags |= v->flags;
- }
- }
-
- for (i=0; i<bptr->outdepth; ++i) {
- v = &(resultjd->var[bptr->outvars[i]]);
- v->flags |= INOUT;
- if (v->type == TYPE_RET)
- v->flags |= PREALLOC;
- else
- v->flags &= ~PREALLOC;
- v->flags &= ~INMEMORY;
- assert(bptr->outvars[i] >= resultjd->localcount);
-
- if (resultjd->interface_map[5*i + v->type].flags == UNUSED) {
- resultjd->interface_map[5*i + v->type].flags = v->flags;
- }
- else {
- resultjd->interface_map[5*i + v->type].flags |= v->flags;
- }
- }
- }
-}
-
-
-static void inline_write_exception_handlers(inline_node *master, inline_node *iln)
-{
- basicblock *n_bptr;
- instruction *n_ins;
- inline_node *child;
- builtintable_entry *bte;
- s4 exvar;
- s4 syncvar;
- s4 i;
-
- child = iln->children;
- if (child) {
- do {
- inline_write_exception_handlers(master, child);
- child = child->next;
- } while (child != iln->children);
- }
-
- if (iln->synchronize) {
- /* create the monitorexit handler */
- n_bptr = create_block(master, iln, iln,
- iln->n_passthroughcount + 1);
- n_bptr->type = BBTYPE_EXH;
- n_bptr->flags = BBFINISHED;
-
- exvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0);
- n_bptr->invars[iln->n_passthroughcount] = exvar;
-
- iln->handler_monitorexit = n_bptr;
-
- /* ACONST / ALOAD */
-
- n_ins = master->inlined_iinstr_cursor++;
- if (iln->m->flags & ACC_STATIC) {
- n_ins->opc = ICMD_ACONST;
- n_ins->sx.val.c.cls = iln->m->clazz;
- n_ins->flags.bits = INS_FLAG_CLASS;
- }
- else {
- n_ins->opc = ICMD_ALOAD;
- n_ins->s1.varindex = iln->synclocal;
- assert(n_ins->s1.varindex != UNUSED);
- }
- /* XXX could be PREALLOCed for builtin call */
- syncvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0);
- n_ins->dst.varindex = syncvar;
- n_ins->line = 0;
-
- /* MONITOREXIT */
-
- bte = builtintable_get_internal(LOCK_monitor_exit);
-
- n_ins = master->inlined_iinstr_cursor++;
- n_ins->opc = ICMD_BUILTIN;
- n_ins->s1.argcount = 1 + iln->n_passthroughcount + 1;
- n_ins->sx.s23.s2.args = DMNEW(s4, n_ins->s1.argcount);
- n_ins->sx.s23.s2.args[0] = syncvar;
- for (i=0; i < iln->n_passthroughcount + 1; ++i) {
- n_ins->sx.s23.s2.args[1 + i] = n_bptr->invars[i];
- }
- n_ins->sx.s23.s3.bte = bte;
- n_ins->line = 0;
-
- /* ATHROW */
-
- n_ins = master->inlined_iinstr_cursor++;
- n_ins->opc = ICMD_ATHROW;
- n_ins->flags.bits = 0;
- n_ins->s1.varindex = exvar;
- n_ins->line = 0;
-
- /* close basic block */
-
- close_block(iln, iln, n_bptr, iln->n_passthroughcount);
- n_bptr->icount = 3;
- }
-}
-
-
-/* second pass driver *********************************************************/
-
-static bool inline_transform(inline_node *iln, jitdata *jd)
-{
- instruction *n_ins;
- basicblock *n_bb;
- basicblock *n_bptr;
- exception_entry *n_ext;
- exception_entry *prevext;
- codegendata *n_cd;
- jitdata *n_jd;
- s4 i;
-#if defined(INLINE_VERIFY_RESULT)
- static int debug_verify_inlined_code = 1;
-#endif
-#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG)
- static int debug_counter = 0;
-#endif
-
- DOLOG( dump_inline_tree(iln, 0); );
-
- assert(iln && jd);
-
- n_ins = DMNEW(instruction, iln->cumul_instructioncount);
- MZERO(n_ins, instruction, iln->cumul_instructioncount);
- iln->inlined_iinstr = n_ins;
-
- n_bb = DMNEW(basicblock, iln->cumul_basicblockcount);
- MZERO(n_bb, basicblock, iln->cumul_basicblockcount);
- iln->inlined_basicblocks = n_bb;
-
- iln->ctx->blockmap = DMNEW(inline_block_map, iln->cumul_blockmapcount);
-
- n_jd = jit_jitdata_new(iln->m);
- n_jd->flags = jd->flags;
- n_jd->code->optlevel = jd->code->optlevel;
- iln->ctx->resultjd = n_jd;
-
- reg_setup(n_jd);
-
- /* create the local_map */
-
- n_jd->local_map = DMNEW(s4, 5*iln->cumul_maxlocals);
- for (i=0; i<5*iln->cumul_maxlocals; ++i)
- n_jd->local_map[i] = UNUSED;
-
- /* create / coalesce local variables */
-
- n_jd->varcount = 0;
- n_jd->vartop = 0;
- n_jd->var = NULL;
-
- inline_locals(iln);
-
- n_jd->localcount = n_jd->vartop;
-
- /* extra variables for verification (debugging) */
-
-#if defined(INLINE_VERIFY_RESULT)
- if (debug_verify_inlined_code) {
- n_jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + 100 /* XXX m->maxstack */;
- if (n_jd->vartop > n_jd->varcount) {
- /* XXX why? */
- n_jd->var = DMREALLOC(n_jd->var, varinfo, n_jd->varcount, n_jd->vartop);
- n_jd->varcount = n_jd->vartop;
- }
- }
-#endif /* defined(INLINE_VERIFY_RESULT) */
-
- /* write inlined code */
-
- inline_rewrite_method(iln);
-
- /* create exception handlers */
-
- inline_write_exception_handlers(iln, iln);
-
- /* write the dummy end block */
-
- n_bptr = create_block(iln, iln, iln, 0);
- n_bptr->flags = BBUNDEF;
- n_bptr->type = BBTYPE_STD;
-
- /* store created code in jitdata */
-
- n_jd->basicblocks = iln->inlined_basicblocks;
- n_jd->instructioncount = iln->cumul_instructioncount;
- n_jd->instructions = iln->inlined_iinstr;
-
- /* link the basic blocks (dummy end block is not counted) */
-
- n_jd->basicblockcount = (iln->inlined_basicblocks_cursor - iln->inlined_basicblocks) - 1;
- for (i=0; i<n_jd->basicblockcount + 1; ++i)
- n_jd->basicblocks[i].next = &(n_jd->basicblocks[i+1]);
- if (i)
- n_jd->basicblocks[i-1].next = NULL;
-
- /* check basicblock numbers */
-
-#if !defined(NDEBUG)
- jit_check_basicblock_numbers(n_jd);
-#endif
-
- /* create the exception table */
-
- if (iln->cumul_exceptiontablelength) {
- exception_entry *tableend;
-
- n_ext = DMNEW(exception_entry, iln->cumul_exceptiontablelength);
- prevext = NULL;
- tableend = inline_exception_tables(iln, n_ext, &prevext);
- assert(tableend == n_ext + iln->cumul_exceptiontablelength);
- if (prevext)
- prevext->down = NULL;
-
- n_jd->exceptiontablelength = iln->cumul_exceptiontablelength;
- n_jd->exceptiontable = n_ext;
- }
- else {
- n_ext = NULL;
- }
-
- /*******************************************************************************/
-
- n_cd = n_jd->cd;
- memcpy(n_cd, jd->cd, sizeof(codegendata));
-
- n_cd->method = NULL; /* XXX */
- n_jd->maxlocals = iln->cumul_maxlocals;
- n_jd->maxinterfaces = iln->ctx->maxinoutdepth;
-
- inline_post_process(n_jd);
-
- inline_interface_variables(iln);
-
- /* for debugging, verify the inlined result */
-
-#if defined(INLINE_VERIFY_RESULT)
- if (debug_verify_inlined_code) {
- debug_verify_inlined_code = 0;
- DOLOG( printf("VERIFYING INLINED RESULT...\n"); fflush(stdout); );
- if (!typecheck(n_jd)) {
- exceptions_clear_exception();
- DOLOG( printf("XXX INLINED RESULT DID NOT PASS VERIFIER XXX\n") );
- return false;
- }
- else {
- DOLOG( printf("VERIFICATION PASSED.\n") );
- }
- debug_verify_inlined_code = 1;
- }
-#endif /* defined(INLINE_VERIFY_RESULT) */
-
- /* we need bigger free memory stacks (XXX these should not be allocated in reg_setup) */
-
- n_jd->rd->freemem = DMNEW(s4, iln->ctx->maxinoutdepth + 1000) /* XXX max vars/block */;
-
-#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG)
- if ( (n_jd->instructioncount >= opt_InlineMinSize)
- && (n_jd->instructioncount <= opt_InlineMaxSize))
- {
- if (debug_counter < opt_InlineCount)
-#endif /* defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) */
- {
- /* install the inlined result */
-
- *jd->code = *n_jd->code;
- n_jd->code = jd->code;
- *jd = *n_jd;
-
- /* statistics and logging */
-
-#if !defined(NDEBUG)
- inline_stat_roots++;
-
- DOLOG_SHORT(
- printf("==== %d.INLINE ==================================================================\n",
- debug_counter);
- printf("\ninline tree:\n");
- dump_inline_tree(iln, 0);
- n_jd->flags |= JITDATA_FLAG_SHOWINTERMEDIATE | JITDATA_FLAG_SHOWDISASSEMBLE;
- /* debug_dump_inlined_code(iln, n_method, n_cd, n_rd); */
- printf("-------- DONE -----------------------------------------------------------\n");
- fflush(stdout);
- );
-#endif
- }
-
-#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG)
- debug_counter++;
- }
-#endif
- return true;
-}
-
-
-/******************************************************************************/
-/* FIRST PASS: build inlining tree */
-/******************************************************************************/
-
-
-/* inline_pre_parse_heuristics *************************************************
-
- Perform heuristic checks whether a call site should be inlined.
- These checks are evaluated before the callee has been parsed and analysed.
-
- IN:
- caller...........inlining node of the caller
- callee...........the called method
- site.............information on the call site
-
- RETURN VALUE:
- true........consider for inlining
- false.......don't inline
-
-*******************************************************************************/
-
-static bool inline_pre_parse_heuristics(const inline_node *caller,
- const methodinfo *callee,
- inline_site *site)
-{
-#if defined(INLINE_MAX_DEPTH)
- if (caller->depth >= INLINE_MAX_DEPTH)
- return false;
-#endif
-
- return true;
-}
-
-
-/* inline_post_parse_heuristics ************************************************
-
- Perform heuristic checks whether a call site should be inlined.
- These checks are evaluated after the callee has been parsed and analysed.
-
- IN:
- caller...........inlining node of the caller (const)
- callee...........the called method (const)
-
- RETURN VALUE:
- true........consider for inlining
- false.......don't inline
-
-*******************************************************************************/
-
-static bool inline_post_parse_heuristics(const inline_node *caller,
- const inline_node *callee)
-{
- return true;
-}
-
-
-/* inline_afterwards_heuristics ************************************************
-
- Perform heuristic checks whether a call site should be inlined.
- These checks are evaluated after the inlining plan for the callee has
- been made.
-
- IN:
- caller...........inlining node of the caller (const)
- callee...........the called method (const)
-
- RETURN VALUE:
- true........consider for inlining
- false.......don't inline
-
-*******************************************************************************/
-
-static bool inline_afterwards_heuristics(const inline_node *caller,
- const inline_node *callee)
-{
- inline_node *cumulator;
-
-#if defined(INLINE_DEPTH_FIRST)
- cumulator = caller;
-#else
- cumulator = caller->ctx->master;
-#endif
-
- if (0
-#if defined(INLINE_MAX_BLOCK_EXPANSION)
- || (cumulator->cumul_basicblockcount + callee->cumul_basicblockcount
- > INLINE_MAX_BLOCK_EXPANSION*caller->ctx->master->jd->basicblockcount)
-#endif
-#if defined(INLINE_MAX_ICMD_EXPANSION)
- || (cumulator->cumul_instructioncount + callee->cumul_instructioncount
- > INLINE_MAX_ICMD_EXPANSION*caller->ctx->master->jd->instructioncount)
-#endif
- )
- {
- return false;
- }
-
- return true;
-}
-
-
-/* inline_is_monomorphic *******************************************************
-
- Check if the given call site can be proven to be monomorphic.
-
- IN:
- callee...........the called method
- call.............the invocation instruction
-
- OUT:
- site->speculative.....flags whether the inlining is speculative
- (only defined if return value is true)
-
- RETURN VALUE:
- true if the call site is (currently) monomorphic,
- false if not or unknown
-
-*******************************************************************************/
-
-static bool inline_is_monomorphic(const methodinfo *callee,
- const instruction *call,
- inline_site *site)
-{
- if ((callee->flags & (ACC_STATIC | ACC_FINAL | ACC_PRIVATE)
- || call->opc == ICMD_INVOKESPECIAL))
- {
- site->speculative = false;
- return true;
- }
-
- /* XXX search single implementation for abstract monomorphics */
-
- if ((callee->flags & (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED
- | ACC_ABSTRACT))
- == (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED))
- {
- if (1) {
- DOLOG( printf("SPECULATIVE INLINE: "); method_println((methodinfo*)callee); );
- site->speculative = true;
-
- return true;
- }
- }
-
- /* possibly polymorphic call site */
-
- return false;
-}
-
-
-/* inline_can_inline ***********************************************************
-
- Check if inlining of the given call site is possible.
-
- IN:
- caller...........inlining node of the caller
- callee...........the called method
- call.............the invocation instruction
-
- OUT:
- site->speculative.....flags whether the inlining is speculative
- (only defined if return value is true)
-
- RETURN VALUE:
- true if inlining is possible, false if not
-
-*******************************************************************************/
-
-static bool inline_can_inline(const inline_node *caller,
- const methodinfo *callee,
- const instruction *call,
- inline_site *site)
-{
- const inline_node *active;
-
- /* cannot inline native methods */
-
- if (callee->flags & ACC_NATIVE)
- return false;
-
- /* cannot inline possibly polymorphic calls */
-
- if (!inline_is_monomorphic(callee, call, site))
- return false;
-
- /* cannot inline recursive calls */
-
- for (active = caller; active; active = active->parent) {
- if (callee == active->m) {
- DOLOG( printf("RECURSIVE!\n") );
- return false;
- }
- }
-
- /* inlining is possible */
-
- return true;
-}
-
-
-/* inline_create_callee_node ***************************************************
-
- Create an inlining node for the given callee.
-
- IN:
- caller...........inlining node of the caller (const)
- callee...........the called method
-
- RETURN VALUE:
- the new inlining node
-
-*******************************************************************************/
-
-static inline_node * inline_create_callee_node(const inline_node *caller,
- methodinfo *callee)
-{
- inline_node *cn; /* the callee inline_node */
-
- cn = DNEW(inline_node);
- MZERO(cn, inline_node, 1);
-
- cn->depth = caller->depth + 1;
- cn->ctx = caller->ctx;
- cn->m = callee;
- cn->synchronize = (callee->flags & ACC_SYNCHRONIZED);
- cn->isstatic = (callee->flags & ACC_STATIC);
-
- return cn;
-}
-
-
-/* inline_set_callee_properties ************************************************
-
- Set properties of the inlined call site.
-
- IN:
- caller...........inlining node of the caller (const)
- cn...............the called method
- site.............info about the call site (const)
-
- OUT:
- *cn..............has the properties set
-
-*******************************************************************************/
-
-static void inline_set_callee_properties(const inline_node *caller,
- inline_node *cn,
- const inline_site *site)
-{
- s4 argi;
- s4 i, j;
- basicblock *bptr;
-
- /* set info about the call site */
-
- cn->callerblock = site->bptr;
- cn->callerins = site->iptr;
- cn->callerpc = site->pc;
- cn->o_handlers = site->handlers;
- cn->n_handlercount = caller->n_handlercount + site->nhandlers;
-
- /* determine if we need basic block boundaries before/after */
-
- cn->blockbefore = false;
- cn->blockafter = false;
-
- if (cn->jd->branchtoentry)
- cn->blockbefore = true;
-
- if (cn->jd->branchtoend)
- cn->blockafter = true;
-
- if (cn->jd->returncount > 1)
- cn->blockafter = true;
-
- /* XXX make safer and reusable (maybe store last real block) */
- for (bptr = cn->jd->basicblocks; bptr && bptr->next && bptr->next->next; bptr = bptr->next)
- ;
-
- if (cn->jd->returnblock != bptr)
- cn->blockafter = true;
-
- /* info about the callee */
-
- cn->localsoffset = caller->localsoffset + caller->m->maxlocals;
- cn->prolog_instructioncount = cn->m->parseddesc->paramcount + 2;
- cn->epilog_instructioncount = 1; /* INLINE_END */
- cn->extra_instructioncount = 0;
-
- /* we need a CHECKNULL for instance methods, except for <init> */
-
- if (!cn->isstatic && cn->m->name != utf_init)
- cn->prolog_instructioncount += 1;
-
- /* deal with synchronized callees */
-
- if (cn->synchronize) {
- methoddesc *md;
- builtintable_entry *bte;
-
- /* we need basic block boundaries because of the handler */
-
- cn->blockbefore = true;
- cn->blockafter = true;
-
- /* for synchronized static methods */
- /* we need an ACONST, MONITORENTER in the prolog */
- /* and ACONST, MONITOREXIT in the epilog */
-
- /* for synchronized instance methods */
- /* we need an COPY, MONITORENTER in the prolog */
- /* and MONITOREXIT in the epilog */
-
- if (cn->isstatic) {
- cn->prolog_instructioncount += 2;
- cn->epilog_instructioncount += 2;
- }
- else {
- cn->prolog_instructioncount += 2;
- cn->epilog_instructioncount += 1;
- cn->localsoffset += 1;
- }
-
- /* and exception handler */
- /* ALOAD, builtin_monitorexit, ATHROW */
-
- cn->extra_instructioncount += 3;
-
- /* exception table entries */
-
- cn->extra_exceptiontablelength = 1 + cn->n_handlercount;
-
- /* add exception handler block */
-
- cn->cumul_basicblockcount_root++;
-
- /* we must call the builtins */
-
- bte = builtintable_get_internal(LOCK_monitor_enter);
- md = bte->md;
- if (md->memuse > cn->regdata->memuse)
- cn->regdata->memuse = md->memuse;
- if (md->argintreguse > cn->regdata->argintreguse)
- cn->regdata->argintreguse = md->argintreguse;
-
- bte = builtintable_get_internal(LOCK_monitor_exit);
- md = bte->md;
- if (md->memuse > cn->regdata->memuse)
- cn->regdata->memuse = md->memuse;
- if (md->argintreguse > cn->regdata->argintreguse)
- cn->regdata->argintreguse = md->argintreguse;
- }
-
- /* determine pass-through variables */
-
- i = site->iptr->s1.argcount - cn->m->parseddesc->paramcount; /* max # of pass-though vars */
-
- cn->n_passthroughvars = DMNEW(s4, i);
- j = 0;
- for (argi = site->iptr->s1.argcount - 1; argi >= cn->m->parseddesc->paramcount; --argi) {
- s4 idx = site->iptr->sx.s23.s2.args[argi];
- if (idx >= caller->jd->localcount) {
- cn->n_passthroughvars[j] = idx;
- j++;
- }
- else {
- DOLOG( printf("PASSING THROUGH LOCAL VARIABLE %d\n", idx); );
- }
- }
- assert(j <= i);
- cn->n_selfpassthroughcount = j;
- cn->n_passthroughcount = caller->n_passthroughcount + cn->n_selfpassthroughcount;
-}
-
-
-/* inline_cumulate_counters ****************************************************
-
- Cumulate counters after a node has been decided to become inlined.
-
- IN:
- caller...........inlining node of the caller
- callee...........inlining node of the callee (const)
-
- OUT:
- *caller..........gets cumulated values added
-
-*******************************************************************************/
-
-static void inline_cumulate_counters(inline_node *caller,
- const inline_node *cn)
-{
- caller->cumul_instructioncount += cn->prolog_instructioncount;
- caller->cumul_instructioncount += cn->epilog_instructioncount;
- caller->cumul_instructioncount += cn->extra_instructioncount;
- caller->cumul_instructioncount += cn->cumul_instructioncount - 1 /*invoke*/;
-
- caller->cumul_basicblockcount += cn->cumul_basicblockcount;
- caller->cumul_basicblockcount_root += cn->cumul_basicblockcount_root;
- caller->cumul_blockmapcount += cn->cumul_blockmapcount;
- caller->cumul_exceptiontablelength += cn->cumul_exceptiontablelength;
- caller->cumul_exceptiontablelength += cn->extra_exceptiontablelength;
-
- if (cn->cumul_maxlocals > caller->cumul_maxlocals)
- caller->cumul_maxlocals = cn->cumul_maxlocals;
-
- /* XXX extra block after inlined call */
- if (cn->blockafter) {
- caller->cumul_basicblockcount += 1;
- caller->cumul_blockmapcount += 1;
- }
-}
-
-
-/* inline_analyse_callee *******************************************************
-
- Analyse an inlining candidate callee.
-
- IN:
- caller...........inlining node of the caller
- callee...........the called method
- site.............info about the call site
-
- OUT:
- site->inlined....true if the callee has been selected for inlining
-
- RETURN VALUE:
- the inline node of the callee, or
- NULL if an error has occurred (don't use the inlining plan in this case)
-
-*******************************************************************************/
-
-static inline_node * inline_analyse_callee(inline_node *caller,
- methodinfo *callee,
- inline_site *site)
-{
- inline_node *cn; /* the callee inline_node */
-
- /* create an inline tree node */
-
- cn = inline_create_callee_node(caller, callee);
-
- /* get the intermediate representation of the callee */
-
- if (!inline_jit_compile(cn))
- return NULL;
-
- /* evaluate heuristics after parsing the callee */
-
- if (!inline_post_parse_heuristics(caller, cn))
- return cn;
-
- /* the call site will be inlined */
-
- site->inlined = true;
-
- /* set info about the call site */
-
- inline_set_callee_properties(caller, cn, site);
-
- /* insert the node into the inline tree */
-
- inline_insert_inline_node(caller, cn);
-
- /* analyse recursively */
-
- if (!inline_analyse_code(cn))
- return NULL;
-
- if (!inline_afterwards_heuristics(caller, cn)) {
-#if defined(INLINE_CANCEL_ON_THRESHOLD)
- return NULL;
-#else
- inline_remove_inline_node(caller, cn);
- caller->ctx->stopped = true;
- site->inlined = false;
- return cn;
-#endif
- }
-
- /* cumulate counters */
-
-#if defined(INLINE_DEPTH_FIRST)
- inline_cumulate_counters(caller, cn);
-#endif
-
-#if defined(INLINE_BREADTH_FIRST)
- while (caller) {
- inline_cumulate_counters(caller, cn);
- caller = caller->parent;
- }
-#endif
-
- return cn;
-}
-
-
-/* inline_process_candidate ****************************************************
-
- Process a selected inlining candidate.
-
- IN:
- cand.............the candidate
-
- RETURN VALUE:
- true........everything ok
- false.......an error has occurred, don't use the plan
-
-*******************************************************************************/
-
-static bool inline_process_candidate(inline_candidate *cand)
-{
- inline_node *cn;
-
- cn = inline_analyse_callee(cand->caller,
- cand->callee,
- &(cand->site));
-
- if (!cn)
- return false;
-
- if (!cand->site.inlined)
- return true;
-
- /* store assumptions */
-
- if (cand->site.speculative)
- method_add_assumption_monomorphic(cand->callee, cand->caller->ctx->master->m);
-
- return true;
-}
-
-
-/* inline_analyse_code *********************************************************
-
- Analyse the intermediate code of the given inlining node.
-
- IN:
- iln..............the inlining node
-
- OUT:
- *iln.............the inlining plan
-
- RETURN VALUE:
- true........everything ok
- false.......an error has occurred, don't use the plan
-
-*******************************************************************************/
-
-static bool inline_analyse_code(inline_node *iln)
-{
- methodinfo *m;
- basicblock *bptr;
- s4 len;
- instruction *iptr;
- methodinfo *callee;
- exception_entry **handlers;
- exception_entry *ex;
- s4 nhandlers;
- s4 blockendpc;
- jitdata *mjd;
- inline_site site;
-
- assert(iln);
-
- m = iln->m;
- mjd = iln->jd;
-
- /* initialize cumulative counters */
-
- iln->cumul_maxlocals = iln->localsoffset + m->maxlocals;
- iln->cumul_exceptiontablelength += mjd->exceptiontablelength;
-
- /* iterate over basic blocks */
-
- blockendpc = 0;
-
- for (bptr = mjd->basicblocks; bptr; bptr = bptr->next) {
-
- /* count the block */
- /* ignore dummy end blocks (but count them for the blockmap) */
-
- iln->cumul_blockmapcount++;
- if ((bptr != mjd->basicblocks || iln->blockbefore)
- &&
- (bptr->icount > 0 || bptr->next != NULL))
- iln->cumul_basicblockcount++;
-
- /* skip dead code */
-
- if (bptr->flags < BBREACHED)
- continue;
-
- /* allocate the buffer of active exception handlers */
- /* XXX this wastes some memory, but probably it does not matter */
-
- handlers = DMNEW(exception_entry*, mjd->exceptiontablelength + 1);
-
- /* determine the active exception handlers for this block */
- /* XXX maybe the handlers of a block should be part of our IR */
- /* XXX this should share code with the type checkers */
- nhandlers = 0;
- for (ex = mjd->exceptiontable; ex ; ex = ex->down) {
- if ((ex->start->nr <= bptr->nr) && (ex->end->nr > bptr->nr)) {
- handlers[nhandlers++] = ex;
- }
- }
- handlers[nhandlers] = NULL;
-
- len = bptr->icount;
- iptr = bptr->iinstr;
-
- blockendpc += len;
- iln->cumul_instructioncount += len;
-
- /* iterate over the instructions of the block */
-
- for (; --len >= 0; ++iptr) {
-
- switch (iptr->opc) {
- case ICMD_INVOKEVIRTUAL:
- case ICMD_INVOKESPECIAL:
- case ICMD_INVOKESTATIC:
- case ICMD_INVOKEINTERFACE:
-
- if (!INSTRUCTION_IS_UNRESOLVED(iptr) && !iln->ctx->stopped) {
- callee = iptr->sx.s23.s3.fmiref->p.method;
-
- if (inline_can_inline(iln, callee, iptr, &site)) {
- site.inlined = false;
- site.bptr = bptr;
- site.iptr = iptr;
- site.pc = blockendpc - len - 1;
- site.handlers = handlers;
- site.nhandlers = nhandlers;
-
- if (inline_pre_parse_heuristics(iln, callee, &site)) {
-#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)
- inline_add_candidate(iln->ctx, iln, callee, &site);
-#else
- inline_candidate cand;
- cand.caller = iln;
- cand.callee = callee;
- cand.site = site;
-
- if (!inline_process_candidate(&cand))
- return false;
-#endif
- }
- }
- }
- break;
-
- case ICMD_RETURN:
- case ICMD_IRETURN:
- case ICMD_ARETURN:
- case ICMD_LRETURN:
- case ICMD_FRETURN:
- case ICMD_DRETURN:
- /* extra ICMD_MOVE may be necessary */
- iln->cumul_instructioncount++;
- break;
- }
- }
-
- /* end of basic block */
- }
-
- return true;
-}
-
-
-static void inline_cumulate_counters_recursive(inline_node *iln)
-{
- inline_node *child;
-
- child = iln->children;
- if (child) {
- do {
- inline_cumulate_counters_recursive(child);
- inline_cumulate_counters(iln, child);
- child = child->next;
- } while (child != iln->children);
- }
-}
-
-
-/* inline_make_inlining_plan ***************************************************
-
- Make an inlining plan for the given root node
-
- IN:
- iln..............the root node
-
- OUT:
- *iln.............the inlining plan
-
- RETURN VALUE:
- true........everything ok
- false.......an error has occurred, don't use the plan
-
-*******************************************************************************/
-
-#if defined(INLINE_KNAPSACK)
-static bool inline_make_inlining_plan(inline_node *iln)
-{
- inline_candidate *cand;
-#if defined(INLINE_COST_BUDGET)
- s4 budget = INLINE_COST_BUDGET;
-# define BUDGETMEMBER cost
-#endif
-#if defined(INLINE_WEIGHT_BUDGET)
- double budget = INLINE_WEIGHT_BUDGET;
-# define BUDGETMEMBER weight
-#endif
-
- inline_analyse_code(iln);
-
- DOLOG( printf("candidates in "); method_println(iln->m);
- inline_candidates_println(iln->ctx); );
-
- while ((cand = inline_pick_best_candidate(iln->ctx)) != NULL)
- {
- if (cand->BUDGETMEMBER <= budget) {
- DOLOG( printf(" picking: "); inline_candidate_println(cand); );
-
- if (!inline_process_candidate(cand))
- return false;
-
-#if !defined(INLINE_ADD_NEGATIVE_TO_BUDGET)
- if (cand->BUDGETMEMBER > 0)
-#endif
- budget -= cand->BUDGETMEMBER;
- }
- }
-
- inline_cumulate_counters_recursive(iln);
-
- return true;
-}
-#endif /* defined(INLINE_KNAPSACK) */
-
-
-#if defined(INLINE_DEPTH_FIRST)
-static bool inline_make_inlining_plan(inline_node *iln)
-{
- return inline_analyse_code(iln);
-}
-#endif /* defined(INLINE_DEPTH_FIRST) */
-
-
-#if defined(INLINE_BREADTH_FIRST)
-static bool inline_make_inlining_plan(inline_node *iln)
-{
- inline_candidate *cand;
-
- inline_analyse_code(iln);
-
- DOLOG( printf("candidates in "); method_println(iln->m);
- inline_candidates_println(iln->ctx); );
-
- while (!iln->ctx->stopped
- && (cand = inline_pick_best_candidate(iln->ctx)) != NULL)
- {
- DOLOG( printf(" picking: "); inline_candidate_println(cand); );
-
- if (!inline_process_candidate(cand))
- return false;
- }
-
- return true;
-}
-#endif /* defined(INLINE_BREADTH_FIRST) */
-
-
-/* statistics *****************************************************************/
-
-#if defined(INLINE_STATISTICS)
-static void inline_gather_statistics_recursive(inline_node *iln)
-{
- inline_node *child;
-
- inline_stat_inlined_nodes++;
-
- if (iln->depth > inline_stat_max_depth)
- inline_stat_max_depth++;
-
- child = iln->children;
- if (child) {
- do {
- inline_gather_statistics_recursive(child);
- child = child->next;
- } while (child != iln->children);
- }
-}
-#endif /* defined(INLINE_STATISTICS) */
-
-
-#if defined(INLINE_STATISTICS)
-static void inline_gather_statistics(inline_node *iln)
-{
- inline_stat_roots_transformed++;
-
- inline_gather_statistics_recursive(iln);
-}
-#endif /* defined(INLINE_STATISTICS) */
-
-
-/* post processing ************************************************************/
-
-#define POSTPROCESS_SRC(varindex) live[varindex]--
-#define POSTPROCESS_DST(varindex) live[varindex]++
-
-#define POSTPROCESS_SRCOP(s) POSTPROCESS_SRC(iptr->s.varindex)
-#define POSTPROCESS_DSTOP(d) POSTPROCESS_DST(iptr->d.varindex)
-
-#define MARKSAVED(varindex) jd->var[varindex].flags |= SAVEDVAR
-
-#define MARK_ALL_SAVED \
- do { \
- for (i=0; i<jd->vartop; ++i) \
- if (live[i]) \
- MARKSAVED(i); \
- } while (0)
-
-static void inline_post_process(jitdata *jd)
-{
- codeinfo *code;
- basicblock *bptr;
- instruction *iptr;
- instruction *iend;
- s4 i;
- icmdtable_entry_t *icmdt;
- s4 *live;
- methoddesc *md;
- builtintable_entry *bte;
-
- /* Get required compiler data. */
-
- code = jd->code;
-
- /* reset the SAVEDVAR flag of all variables */
-
- for (i=0; i<jd->vartop; ++i)
- jd->var[i].flags &= ~SAVEDVAR;
-
- /* allocate the life counters */
-
- live = DMNEW(s4, jd->vartop);
- MZERO(live, s4, jd->vartop);
-
- /* iterate over all basic blocks */
-
- for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) {
- if (bptr->flags < BBREACHED)
- continue;
-
- /* make invars live */
-
- for (i=0; i<bptr->indepth; ++i)
- POSTPROCESS_DST(bptr->invars[i]);
-
- iptr = bptr->iinstr;
- iend = iptr + bptr->icount;
-
- for (; iptr < iend; ++iptr) {
-
- icmdt = &(icmd_table[iptr->opc]);
-
- switch (icmdt->dataflow) {
- case DF_3_TO_0:
- POSTPROCESS_SRCOP(sx.s23.s3);
- case DF_2_TO_0:
- POSTPROCESS_SRCOP(sx.s23.s2);
- case DF_1_TO_0:
- POSTPROCESS_SRCOP(s1);
- case DF_0_TO_0:
- if (icmdt->flags & ICMDTABLE_CALLS) {
- code_unflag_leafmethod(code);
- MARK_ALL_SAVED;
- }
- break;
-
- case DF_2_TO_1:
- POSTPROCESS_SRCOP(sx.s23.s2);
- case DF_1_TO_1:
- case DF_MOVE:
- POSTPROCESS_SRCOP(s1);
- case DF_0_TO_1:
- if (icmdt->flags & ICMDTABLE_CALLS) {
- code_unflag_leafmethod(code);
- MARK_ALL_SAVED;
- }
- case DF_COPY:
- POSTPROCESS_DSTOP(dst);
- break;
-
- case DF_N_TO_1:
- for (i=0; i<iptr->s1.argcount; ++i) {
- POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]);
- }
- if (icmdt->flags & ICMDTABLE_CALLS) {
- code_unflag_leafmethod(code);
- MARK_ALL_SAVED;
- }
- POSTPROCESS_DSTOP(dst);
- break;
-
- case DF_INVOKE:
- INSTRUCTION_GET_METHODDESC(iptr, md);
- post_process_call:
- code_unflag_leafmethod(code);
- for (i=0; i<md->paramcount; ++i) {
- POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]);
- }
- for (; i<iptr->s1.argcount; ++i) {
- MARKSAVED(iptr->sx.s23.s2.args[i]);
- }
- if (md->returntype.type != TYPE_VOID)
- POSTPROCESS_DSTOP(dst);
- break;
-
- case DF_BUILTIN:
- bte = iptr->sx.s23.s3.bte;
- md = bte->md;
- goto post_process_call;
-
- default:
- assert(0);
- }
-
- } /* end instruction loop */
-
- /* consume outvars */
-
- for (i=0; i<bptr->outdepth; ++i)
- POSTPROCESS_SRC(bptr->outvars[i]);
-
-#if !defined(NDEBUG)
- for (i=jd->localcount; i < jd->vartop; ++i)
- assert(live[i] == 0);
-#endif
-
- } /* end basic block loop */
-}
-
-
-/* inline_create_root_node *****************************************************
-
- Create the root node of the inlining tree.
-
- IN:
- jd...............the current jitdata of the root method
-
- RETURN VALUE:
- the root node of the inlining tree
-
-*******************************************************************************/
-
-static inline_node * inline_create_root_node(jitdata *jd)
-{
- inline_node *iln;
-
- iln = DNEW(inline_node);
- MZERO(iln, inline_node, 1);
-
- iln->m = jd->m;
- iln->jd = jd;
- iln->regdata = jd->rd;
-
- iln->blockbefore = true;
- iln->blockafter = true;
-
- iln->cumul_instructioncount = 0;
- iln->cumul_basicblockcount = 1 /* dummy end block */;
-
- /* create inlining context */
-
- iln->ctx = DNEW(inline_context);
- MZERO(iln->ctx, inline_context, 1);
- iln->ctx->master = iln;
- iln->ctx->next_debugnr = 1; /* XXX debug */
-
- return iln;
-}
-
-
-/******************************************************************************/
-/* MAIN DRIVER FUNCTION */
-/******************************************************************************/
-
-bool inline_inline(jitdata *jd)
-{
- inline_node *iln;
-
- DOLOG( printf("==== INLINE ==================================================================\n");
- show_method(jd, SHOW_STACK); );
-
-#if defined(INLINE_STATISTICS)
- inline_stat_roots++;
-#endif
-
- iln = inline_create_root_node(jd);
-
- if (inline_make_inlining_plan(iln)) {
-
- /* add blocks to the root node */
-
- iln->cumul_basicblockcount += iln->cumul_basicblockcount_root;
- iln->cumul_blockmapcount += iln->cumul_basicblockcount_root;
-
- DOLOG( printf("==== INLINE TRANSFORM ========================================================\n"); );
-
- if (iln->children)
- inline_transform(iln, jd);
-
-#if defined(INLINE_STATISTICS)
- inline_gather_statistics(iln);
-#endif
- }
-
- DOLOG( printf("-------- DONE -----------------------------------------------------------\n");
- fflush(stdout); );
-
- return true;
-}
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/inline/inline.c - method inlining
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "threads/lock.hpp"
+#include "threads/mutex.hpp"
+#include "threads/thread.hpp"
+
+#include "toolbox/logging.h"
+
+#include "vm/jit/builtin.hpp"
+#include "vm/class.hpp"
+#include "vm/global.h"
+#include "vm/initialize.hpp"
+#include "vm/method.hpp"
+#include "vm/options.h"
+#include "vm/statistics.h"
+
+#include "vm/jit/jit.hpp"
+#include "vm/jit/parse.hpp"
+#include "vm/jit/reg.h"
+#include "vm/jit/show.hpp"
+#include "vm/jit/stack.h"
+
+#include "vm/jit/inline/inline.hpp"
+#include "vm/jit/loop/loop.h"
+
+#include "vm/jit/verify/typecheck.hpp"
+
+
+/* algorithm tuning constants *************************************************/
+
+/* Algorithm Selection */
+/* Define exactly one of the following three to select the inlining */
+/* heuristics. */
+
+/*#define INLINE_DEPTH_FIRST*/
+/*#define INLINE_BREADTH_FIRST*/
+#define INLINE_KNAPSACK
+
+/* Parameters for knapsack heuristics: */
+
+#if defined(INLINE_KNAPSACK)
+
+#define INLINE_COUNTDOWN_INIT 1000
+#define INLINE_COST_OFFSET -16
+#define INLINE_COST_BUDGET 100
+/*#define INLINE_WEIGHT_BUDGET 5.0*/
+/*#define INLINE_ADD_NEGATIVE_TO_BUDGET*/
+/*#define INLINE_MAX_DEPTH 3*/
+/*#define INLINE_DIVIDE_COST_BY_FREQ */
+
+#endif
+
+/* Parameters for depth-first heuristics: */
+
+#if defined(INLINE_DEPTH_FIRST)
+
+#define INLINE_MAX_DEPTH 3
+#define INLINE_MAX_BLOCK_EXPANSION 10
+/*#define INLINE_MAX_ICMD_EXPANSION 10*/
+/*#define INLINE_CANCEL_ON_THRESHOLD*/
+
+#endif
+
+/* Parameters for breadth-first heuristics: */
+
+#if defined(INLINE_BREADTH_FIRST)
+
+/*#define INLINE_MAX_BLOCK_EXPANSION 10*/
+#define INLINE_MAX_ICMD_EXPANSION 5
+
+#endif
+
+
+/* debugging ******************************************************************/
+
+#if !defined(NDEBUG)
+#define INLINE_VERBOSE
+#define DOLOG(code) do{ if (opt_TraceInlining >= 2) { code; } }while(0)
+#define DOLOG_SHORT(code) do{ if (opt_TraceInlining >= 1) { code; } }while(0)
+#else
+#define DOLOG(code)
+#endif
+
+#if defined(ENABLE_VERIFIER) && !defined(NDEBUG)
+/* Define this to verify the resulting code after inlining. */
+/* Note: This is only useful for development and may require patches to the */
+/* verifier code. */
+/* #define INLINE_VERIFY_RESULT */
+#endif
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* types **********************************************************************/
+
+typedef struct inline_node inline_node;
+typedef struct inline_target_ref inline_target_ref;
+typedef struct inline_context inline_context;
+typedef struct inline_block_map inline_block_map;
+typedef struct inline_site inline_site;
+typedef struct inline_candidate inline_candidate;
+
+struct inline_node {
+ inline_context *ctx;
+
+ jitdata *jd;
+ methodinfo *m;
+ inline_node *children;
+ inline_node *next; /* next node at this depth */
+ inline_node *prev; /* prev node at this depth */
+ int depth; /* inlining depth, 0 for root */
+
+ /* info about the call site (if depth > 0)*/
+ inline_node *parent; /* node of the caller (NULL for root) */
+ basicblock *callerblock; /* original block containing the INVOKE* */
+ instruction *callerins; /* the original INVOKE* instruction */
+ s4 callerpc;
+ s4 *n_passthroughvars;
+ int n_passthroughcount;
+ int n_selfpassthroughcount; /* # of pass-through vars of the call itself */
+ exception_entry **o_handlers;
+ int n_handlercount; /* # of handlers protecting this call */
+ int n_resultlocal;
+ int synclocal; /* variable used for synchr., or UNUSED */
+ bool isstatic; /* this is a static call */
+
+ bool blockbefore; /* block boundary before inlined body? */
+ bool blockafter; /* block boundary after inlined body? */
+
+ /* info about the callee */
+ int localsoffset;
+ int prolog_instructioncount; /* # of ICMDs in the inlining prolog */
+ int epilog_instructioncount; /* # of ICMDs in the inlining epilog */
+ int extra_instructioncount;
+ int extra_exceptiontablelength; /* # of extra handlers to put in caller */
+ bool synchronize; /* do we have to synchronize enter/exit? */
+ basicblock *handler_monitorexit; /* handler for synchronized inlinees */
+ s4 *varmap;
+
+ /* cumulative values */
+ int cumul_instructioncount; /* ICMDs in this node and its children */
+ int cumul_basicblockcount; /* BBs started by this node and its children */
+ int cumul_basicblockcount_root; /* BBs that have to be added to the root */
+ /* node if this node is inlined */
+ int cumul_blockmapcount;
+ int cumul_maxlocals;
+ int cumul_exceptiontablelength;
+
+ /* output */
+ instruction *inlined_iinstr;
+ instruction *inlined_iinstr_cursor;
+ basicblock *inlined_basicblocks;
+ basicblock *inlined_basicblocks_cursor;
+
+ /* register data */
+ registerdata *regdata;
+
+ /* temporary */
+ inline_target_ref *refs;
+ instruction *inline_start_instruction;
+ s4 *javalocals;
+
+ /* XXX debug */
+ char *indent;
+ int debugnr;
+};
+
+struct inline_target_ref {
+ inline_target_ref *next;
+ union {
+ basicblock **block;
+ s4 *nr;
+ } ref;
+ basicblock *target;
+ bool isnumber;
+};
+
+struct inline_block_map {
+ inline_node *iln;
+ basicblock *o_block;
+ basicblock *n_block;
+};
+
+struct inline_context {
+ inline_node *master;
+
+ jitdata *resultjd;
+
+ inline_candidate *candidates;
+
+ int next_block_number;
+ inline_block_map *blockmap;
+ int blockmap_index;
+
+ int maxinoutdepth;
+
+ bool stopped;
+
+ int next_debugnr; /* XXX debug */
+};
+
+struct inline_site {
+ bool speculative; /* true, if inlining would be speculative */
+ bool inlined; /* true, if this site has been inlined */
+
+ basicblock *bptr; /* basic block containing the call site */
+ instruction *iptr; /* the invocation instruction */
+ exception_entry **handlers; /* active handlers at the call site */
+ s4 nhandlers; /* number of active handlers */
+ s4 pc; /* PC of the invocation instruction */
+};
+
+struct inline_candidate {
+ inline_candidate *next;
+ int freq;
+ int cost;
+ double weight;
+ inline_node *caller;
+ methodinfo *callee;
+ inline_site site;
+};
+
+
+/* prototypes *****************************************************************/
+
+static bool inline_analyse_code(inline_node *iln);
+static void inline_post_process(jitdata *jd);
+
+
+/* debug helpers **************************************************************/
+
+#if !defined(NDEBUG)
+#include "inline_debug.inc"
+#endif
+
+
+/* statistics *****************************************************************/
+
+/*#define INLINE_STATISTICS*/
+
+#if !defined(NDEBUG)
+#define INLINE_STATISTICS
+#endif
+
+#if defined(INLINE_STATISTICS)
+int inline_stat_roots = 0;
+int inline_stat_roots_transformed = 0;
+int inline_stat_inlined_nodes = 0;
+int inline_stat_max_depth = 0;
+
+void inline_print_stats()
+{
+ printf("inlining statistics:\n");
+ printf(" roots analysed : %d\n", inline_stat_roots);
+ printf(" roots transformed: %d\n", inline_stat_roots_transformed);
+ printf(" inlined nodes : %d\n", inline_stat_inlined_nodes);
+ printf(" max depth : %d\n", inline_stat_max_depth);
+}
+#endif
+
+
+/* compilation of callees *****************************************************/
+
+static bool inline_jit_compile_intern(jitdata *jd)
+{
+ methodinfo *m;
+
+ /* XXX should share code with jit.c */
+
+ assert(jd);
+
+ /* XXX initialize the static function's class */
+
+ m = jd->m;
+
+ /* call the compiler passes ***********************************************/
+
+ /* call parse pass */
+
+ DOLOG( log_message_class("Parsing ", m->clazz) );
+ if (!parse(jd)) {
+ return false;
+ }
+
+ /* call stack analysis pass */
+
+ if (!stack_analyse(jd)) {
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool inline_jit_compile(inline_node *iln)
+{
+ bool r;
+ methodinfo *m;
+ jitdata *jd;
+
+ /* XXX should share code with jit.c */
+
+ assert(iln);
+ m = iln->m;
+ assert(m);
+
+ /* enter a monitor on the method */
+
+ m->mutex->lock();
+
+ /* allocate jitdata structure and fill it */
+
+ jd = jit_jitdata_new(m);
+ iln->jd = jd;
+
+ jd->flags = 0; /* XXX */
+
+ /* initialize the register allocator */
+
+ reg_setup(jd);
+
+ /* setup the codegendata memory */
+
+ /* XXX do a pseudo setup */
+ jd->cd = (codegendata*) DumpMemory::allocate(sizeof(codegendata));
+ MZERO(jd->cd, codegendata, 1);
+ jd->cd->method = m;
+ /* XXX uses too much dump memory codegen_setup(jd); */
+
+ /* now call internal compile function */
+
+ r = inline_jit_compile_intern(jd);
+
+ if (r) {
+ iln->regdata = jd->rd;
+ }
+
+ /* free some memory */
+#if 0
+
+#if defined(ENABLE_JIT)
+# if defined(ENABLE_INTRP)
+ if (!opt_intrp)
+# endif
+ codegen_free(jd);
+#endif
+
+#endif
+
+ /* leave the monitor */
+
+ m->mutex->unlock();
+
+ return r;
+}
+
+
+/* inlining tree handling *****************************************************/
+
+static void inline_insert_inline_node(inline_node *parent, inline_node *child)
+{
+ inline_node *first;
+ inline_node *succ;
+
+ assert(parent && child);
+
+ child->parent = parent;
+
+ child->debugnr = parent->ctx->next_debugnr++; /* XXX debug */
+
+ first = parent->children;
+ if (!first) {
+ /* insert as only node */
+ parent->children = child;
+ child->next = child;
+ child->prev = child;
+ return;
+ }
+
+ /* {there is at least one child already there} */
+
+ /* XXX is this search necessary, or could we always add at the end? */
+
+ succ = first;
+ while (succ->callerpc < child->callerpc) {
+ succ = succ->next;
+ if (succ == first) {
+ /* insert as last node */
+ child->prev = first->prev;
+ child->next = first;
+ child->prev->next = child;
+ child->next->prev = child;
+ return;
+ }
+ }
+
+ assert(succ->callerpc > child->callerpc);
+
+ /* insert before succ */
+
+ child->prev = succ->prev;
+ child->next = succ;
+ child->prev->next = child;
+ child->next->prev = child;
+
+ if (parent->children == succ)
+ parent->children = child;
+}
+
+
+static void inline_remove_inline_node(inline_node *parent, inline_node *child)
+{
+ assert(parent);
+ assert(child);
+ assert(child->parent == parent);
+
+ if (child->prev == child) {
+ /* remove the only child node */
+ parent->children = NULL;
+ }
+ else {
+ child->prev->next = child->next;
+ child->next->prev = child->prev;
+
+ if (parent->children == child)
+ parent->children = child->next;
+ }
+}
+
+
+/* inlining candidate handling ************************************************/
+
+#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)
+static void inline_add_candidate(inline_context *ctx,
+ inline_node *caller,
+ methodinfo *callee,
+ inline_site *site)
+{
+ inline_candidate **link;
+ inline_candidate *cand;
+
+ cand = (inline_candidate*) DumpMemory::allocate(sizeof(inline_candidate));
+#if defined(INLINE_DIVIDE_COST_BY_FREQ)
+ cand->freq = INLINE_COUNTDOWN_INIT - callee->hitcountdown;
+ if (cand->freq < 1)
+#endif
+ cand->freq = 1;
+#if defined(INLINE_KNAPSACK)
+ cand->cost = callee->jcodelength + INLINE_COST_OFFSET;
+#endif
+#if defined(INLINE_BREADTH_FIRST)
+ cand->cost = caller->depth;
+#endif
+ cand->caller = caller;
+ cand->callee = callee;
+ cand->site = *site;
+
+ cand->weight = (double)cand->cost / cand->freq;
+
+ for (link = &(ctx->candidates); ; link = &((*link)->next)) {
+ if (!*link || (*link)->weight > cand->weight) {
+ cand->next = *link;
+ *link = cand;
+ break;
+ }
+ }
+}
+#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */
+
+#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)
+static inline_candidate * inline_pick_best_candidate(inline_context *ctx)
+{
+ inline_candidate *cand;
+
+ cand = ctx->candidates;
+
+ if (cand)
+ ctx->candidates = cand->next;
+
+ return cand;
+}
+#endif /* defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST) */
+
+#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST))
+static void inline_candidate_println(inline_candidate *cand)
+{
+ printf("%10g (%5d / %5d) depth %2d ",
+ cand->weight, cand->cost, cand->freq, cand->caller->depth + 1);
+ method_println(cand->callee);
+}
+#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */
+
+
+#if !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST))
+static void inline_candidates_println(inline_context *ctx)
+{
+ inline_candidate *cand;
+
+ for (cand = ctx->candidates; cand != NULL; cand = cand->next) {
+ printf(" ");
+ inline_candidate_println(cand);
+ }
+}
+#endif /* !defined(NDEBUG) && (defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)) */
+
+
+/* variable handling **********************************************************/
+
+static s4 inline_new_variable(jitdata *jd, s4 type, s4 flags)
+{
+ s4 index;
+ s4 newcount;
+
+ index = jd->vartop++;
+ if (index >= jd->varcount) {
+ newcount = jd->vartop * 2; /* XXX */
+ jd->var = (varinfo*) DumpMemory::reallocate(jd->var, sizeof(varinfo) * jd->varcount, sizeof(varinfo) * newcount);
+ MZERO(jd->var + jd->varcount, varinfo, (newcount - jd->varcount));
+ jd->varcount = newcount;
+ }
+
+ jd->var[index].type = type;
+ jd->var[index].flags = flags;
+
+ return index;
+}
+
+
+static s4 inline_new_variable_clone(jitdata *jd, jitdata *origjd, s4 origidx)
+{
+ varinfo *v;
+ s4 newidx;
+
+ v = &(origjd->var[origidx]);
+
+ newidx = inline_new_variable(jd, v->type, v->flags);
+
+ jd->var[newidx].vv = v->vv;
+
+ return newidx;
+}
+
+
+static s4 inline_new_temp_variable(jitdata *jd, s4 type)
+{
+ return inline_new_variable(jd, type, 0);
+}
+
+
+static s4 inline_translate_variable(jitdata *jd, jitdata *origjd, s4 *varmap, s4 index)
+{
+ s4 idx;
+
+ idx = varmap[index];
+
+ if (idx < 0) {
+ idx = inline_new_variable_clone(jd, origjd, index);
+ varmap[index] = idx;
+ }
+
+ return idx;
+}
+
+
+static s4 *create_variable_map(inline_node *callee)
+{
+ s4 *varmap;
+ s4 i, t;
+ s4 varindex;
+ s4 n_javaindex;
+ s4 avail;
+ varinfo *v;
+
+ /* create the variable mapping */
+
+ varmap = (s4*) DumpMemory::allocate(sizeof(s4) * callee->jd->varcount);
+ for (i=0; i<callee->jd->varcount; ++i)
+ varmap[i] = -1;
+
+ /* translate local variables */
+
+ for (i=0; i<callee->m->maxlocals; ++i) {
+ for (t=0; t<5; ++t) {
+ varindex = callee->jd->local_map[5*i + t];
+ if (varindex == UNUSED)
+ continue;
+
+ v = &(callee->jd->var[varindex]);
+ assert(v->type == t || v->type == TYPE_VOID); /* XXX stack leaves VOID */
+ v->type = t; /* XXX restore if it is TYPE_VOID */
+
+ avail = callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t];
+
+ if (avail == UNUSED) {
+ avail = inline_new_variable_clone(callee->ctx->resultjd, callee->jd, varindex);
+ callee->ctx->resultjd->local_map[5*(callee->localsoffset + i) + t] = avail;
+ }
+
+ varmap[varindex] = avail;
+ }
+ }
+
+ /* for synchronized instance methods we need an extra local */
+
+ if (callee->synchronize && !(callee->m->flags & ACC_STATIC)) {
+ n_javaindex = callee->localsoffset - 1;
+ assert(n_javaindex >= 0);
+ assert(callee->parent);
+ assert(n_javaindex == callee->parent->localsoffset + callee->parent->m->maxlocals);
+
+ avail = callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR];
+
+ if (avail == UNUSED) {
+ avail = inline_new_variable(callee->ctx->resultjd, TYPE_ADR, 0);
+ callee->ctx->resultjd->local_map[5*n_javaindex + TYPE_ADR] = avail;
+ }
+
+ callee->synclocal = avail;
+ }
+ else {
+ callee->synclocal = UNUSED;
+ }
+
+ return varmap;
+}
+
+
+/* basic block translation ****************************************************/
+
+#define INLINE_RETURN_REFERENCE(callee) \
+ ( (basicblock *) (ptrint) (0x333 + (callee)->depth) )
+
+
+static void inline_add_block_reference(inline_node *iln, basicblock **blockp)
+{
+ inline_target_ref *ref;
+
+ ref = (inline_target_ref*) DumpMemory::allocate(sizeof(inline_target_ref));
+ ref->ref.block = blockp;
+ ref->isnumber = false;
+ ref->next = iln->refs;
+ iln->refs = ref;
+}
+
+
+#if 0
+static void inline_add_blocknr_reference(inline_node *iln, s4 *nrp)
+{
+ inline_target_ref *ref;
+
+ ref = (inline_target_ref*) DumpMemory::allocate(inline_target_ref);
+ ref->ref.nr = nrp;
+ ref->isnumber = true;
+ ref->next = iln->refs;
+ iln->refs = ref;
+}
+#endif
+
+
+static void inline_block_translation(inline_node *iln, basicblock *o_bptr, basicblock *n_bptr)
+{
+ inline_context *ctx;
+
+ ctx = iln->ctx;
+ assert(ctx->blockmap_index < ctx->master->cumul_blockmapcount);
+
+ ctx->blockmap[ctx->blockmap_index].iln = iln;
+ ctx->blockmap[ctx->blockmap_index].o_block = o_bptr;
+ ctx->blockmap[ctx->blockmap_index].n_block = n_bptr;
+
+ ctx->blockmap_index++;
+}
+
+
+static basicblock * inline_map_block(inline_node *iln,
+ basicblock *o_block,
+ inline_node *targetiln)
+{
+ inline_block_map *bm;
+ inline_block_map *bmend;
+
+ assert(iln);
+ assert(targetiln);
+
+ if (!o_block)
+ return NULL;
+
+ bm = iln->ctx->blockmap;
+ bmend = bm + iln->ctx->blockmap_index;
+
+ while (bm < bmend) {
+ assert(bm->iln && bm->o_block && bm->n_block);
+ if (bm->o_block == o_block && bm->iln == targetiln)
+ return bm->n_block;
+ bm++;
+ }
+
+ assert(false);
+ return NULL; /* not reached */
+}
+
+
+static void inline_resolve_block_refs(inline_target_ref **refs,
+ basicblock *o_bptr,
+ basicblock *n_bptr,
+ bool returnref)
+{
+ inline_target_ref *ref;
+ inline_target_ref *prev;
+
+ prev = NULL;
+ for (ref = *refs; ref != NULL; ref = ref->next) {
+ if (ref->isnumber && !returnref) {
+ if (*(ref->ref.nr) == JAVALOCAL_FROM_RETADDR(o_bptr->nr)) {
+ *(ref->ref.nr) = JAVALOCAL_FROM_RETADDR(n_bptr->nr);
+ goto remove_ref;
+ }
+ }
+ else {
+ if (*(ref->ref.block) == o_bptr) {
+ *(ref->ref.block) = n_bptr;
+ goto remove_ref;
+ }
+ }
+
+ /* skip this ref */
+
+ prev = ref;
+ continue;
+
+remove_ref:
+ /* remove this ref */
+
+ if (prev) {
+ prev->next = ref->next;
+ }
+ else {
+ *refs = ref->next;
+ }
+ }
+}
+
+
+/* basic block creation *******************************************************/
+
+static basicblock * create_block(inline_node *container,
+ inline_node *iln,
+ inline_node *inner,
+ int indepth)
+{
+ basicblock *n_bptr;
+ inline_node *outer;
+ s4 i;
+ s4 depth;
+ s4 varidx;
+ s4 newvaridx;
+
+ assert(container);
+ assert(iln);
+ assert(inner);
+ assert(indepth >= 0);
+
+ n_bptr = container->inlined_basicblocks_cursor++;
+ assert(n_bptr);
+ assert((n_bptr - container->inlined_basicblocks) < container->cumul_basicblockcount);
+
+ BASICBLOCK_INIT(n_bptr, iln->m);
+
+ n_bptr->iinstr = container->inlined_iinstr_cursor;
+ n_bptr->next = n_bptr + 1;
+ n_bptr->nr = container->ctx->next_block_number++;
+ n_bptr->indepth = indepth;
+ n_bptr->flags = BBFINISHED; /* XXX */
+
+ /* set the inlineinfo of the new block */
+
+ if (iln->inline_start_instruction)
+ n_bptr->inlineinfo = iln->inline_start_instruction->sx.s23.s3.inlineinfo;
+
+ if (indepth > container->ctx->maxinoutdepth)
+ container->ctx->maxinoutdepth = indepth;
+
+ if (indepth) {
+ n_bptr->invars = (s4*) DumpMemory::allocate(sizeof(s4) * indepth);
+
+
+ for (i=0; i<indepth; ++i)
+ n_bptr->invars[i] = -1; /* XXX debug */
+
+ /* pass-through variables enter the block */
+
+ outer = inner->parent;
+ while (outer != NULL) {
+ depth = outer->n_passthroughcount;
+
+ assert(depth + inner->n_selfpassthroughcount <= indepth);
+
+ for (i=0; i<inner->n_selfpassthroughcount; ++i) {
+ varidx = inner->n_passthroughvars[i];
+ newvaridx =
+ inline_new_variable_clone(container->ctx->resultjd,
+ outer->jd,
+ varidx);
+ n_bptr->invars[depth + i] = newvaridx;
+ outer->varmap[varidx] = newvaridx;
+ }
+ inner = outer;
+ outer = outer->parent;
+ }
+ }
+ else {
+ n_bptr->invars = NULL;
+ }
+
+ /* XXX for the verifier. should not be here */
+
+ {
+ varinfo *dv;
+
+ dv = (varinfo*) DumpMemory::allocate(sizeof(varinfo) * (iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS));
+ MZERO(dv, varinfo, iln->ctx->resultjd->localcount + VERIFIER_EXTRA_LOCALS);
+ n_bptr->inlocals = dv;
+ }
+
+ return n_bptr;
+}
+
+
+static s4 *translate_javalocals(inline_node *iln, s4 *javalocals)
+{
+ s4 *jl;
+ s4 i, j;
+
+ jl = (s4*) DumpMemory::allocate(sizeof(s4) * iln->jd->maxlocals);
+
+ for (i=0; i<iln->jd->maxlocals; ++i) {
+ j = javalocals[i];
+ if (j > UNUSED)
+ j = inline_translate_variable(iln->ctx->resultjd, iln->jd, iln->varmap, j);
+ jl[i] = j;
+
+#if 0
+ if (j < UNUSED) {
+ /* an encoded returnAddress value - must be relocated */
+ inline_add_blocknr_reference(iln, &(jl[i]));
+ }
+#endif
+ }
+
+ return jl;
+}
+
+
+static basicblock * create_body_block(inline_node *iln,
+ basicblock *o_bptr, s4 *varmap)
+{
+ basicblock *n_bptr;
+ s4 i;
+
+ n_bptr = create_block(iln, iln, iln,
+ o_bptr->indepth + iln->n_passthroughcount);
+
+ n_bptr->type = o_bptr->type;
+ n_bptr->flags = o_bptr->flags;
+ n_bptr->bitflags = o_bptr->bitflags;
+
+ /* resolve references to this block */
+
+ inline_resolve_block_refs(&(iln->refs), o_bptr, n_bptr, false);
+
+ /* translate the invars of the original block */
+
+ for (i=0; i<o_bptr->indepth; ++i) {
+ n_bptr->invars[iln->n_passthroughcount + i] =
+ inline_translate_variable(iln->ctx->resultjd, iln->jd,
+ varmap,
+ o_bptr->invars[i]);
+ }
+
+ /* translate javalocals info (not for dead code) */
+
+ if (n_bptr->flags >= BBREACHED)
+ n_bptr->javalocals = translate_javalocals(iln, o_bptr->javalocals);
+
+ return n_bptr;
+}
+
+
+static basicblock * create_epilog_block(inline_node *caller, inline_node *callee, s4 *varmap)
+{
+ basicblock *n_bptr;
+ s4 retcount;
+ s4 idx;
+
+ /* number of return variables */
+
+ retcount = (callee->n_resultlocal == -1
+ && callee->m->parseddesc->returntype.type != TYPE_VOID) ? 1 : 0;
+
+ /* start the epilog block */
+
+ n_bptr = create_block(caller, caller, callee,
+ callee->n_passthroughcount + retcount);
+
+ /* resolve references to the return block */
+
+ inline_resolve_block_refs(&(callee->refs),
+ INLINE_RETURN_REFERENCE(callee),
+ n_bptr,
+ true);
+
+ /* return variable */
+
+ if (retcount) {
+ idx = inline_new_variable(caller->ctx->resultjd,
+ callee->m->parseddesc->returntype.type, 0 /* XXX */);
+ n_bptr->invars[callee->n_passthroughcount] = idx;
+ varmap[callee->callerins->dst.varindex] = idx;
+ }
+
+ /* set javalocals */
+
+ n_bptr->javalocals = (s4*) DumpMemory::allocate(sizeof(s4) * caller->jd->maxlocals);
+ MCOPY(n_bptr->javalocals, caller->javalocals, s4, caller->jd->maxlocals);
+
+ /* set block flags & type */
+
+ n_bptr->flags = /* XXX original block flags */ BBFINISHED;
+ n_bptr->type = BBTYPE_STD;
+
+ return n_bptr;
+}
+
+
+static void close_block(inline_node *iln, inline_node *inner, basicblock *n_bptr, s4 outdepth)
+{
+ inline_node *outer;
+ s4 i;
+ s4 depth;
+ s4 varidx;
+
+ n_bptr->outdepth = outdepth;
+ n_bptr->outvars = (s4*) DumpMemory::allocate(sizeof(s4) * outdepth);
+
+ for (i=0; i<outdepth; ++i)
+ n_bptr->outvars[i] = 0; /* XXX debug */
+
+ if (outdepth > iln->ctx->maxinoutdepth)
+ iln->ctx->maxinoutdepth = outdepth;
+
+ /* pass-through variables leave the block */
+
+ outer = inner->parent;
+ while (outer != NULL) {
+ depth = outer->n_passthroughcount;
+
+ assert(depth + inner->n_selfpassthroughcount <= outdepth);
+
+ for (i=0; i<inner->n_selfpassthroughcount; ++i) {
+ varidx = inner->n_passthroughvars[i];
+ n_bptr->outvars[depth + i] =
+ inline_translate_variable(iln->ctx->resultjd,
+ outer->jd,
+ outer->varmap,
+ varidx);
+ }
+ inner = outer;
+ outer = outer->parent;
+ }
+}
+
+
+static void close_prolog_block(inline_node *iln,
+ basicblock *n_bptr,
+ inline_node *nextcall)
+{
+ /* XXX add original outvars! */
+ close_block(iln, nextcall, n_bptr, nextcall->n_passthroughcount);
+
+ /* pass-through variables */
+
+ DOLOG( printf("closed prolog block:\n");
+ show_basicblock(iln->ctx->resultjd, n_bptr, SHOW_STACK); );
+}
+
+
+static void close_body_block(inline_node *iln,
+ basicblock *n_bptr,
+ basicblock *o_bptr,
+ s4 *varmap,
+ s4 retcount,
+ s4 retidx)
+{
+ s4 i;
+
+ close_block(iln, iln, n_bptr, iln->n_passthroughcount + o_bptr->outdepth + retcount);
+
+ /* translate the outvars of the original block */
+
+ /* XXX reuse code */
+ for (i=0; i<o_bptr->outdepth; ++i) {
+ n_bptr->outvars[iln->n_passthroughcount + i] =
+ inline_translate_variable(iln->ctx->resultjd, iln->jd, varmap,
+ o_bptr->outvars[i]);
+ }
+
+ /* set the return variable, if any */
+
+ if (retcount) {
+ assert(retidx >= 0);
+ n_bptr->outvars[iln->n_passthroughcount + o_bptr->outdepth] = retidx;
+ }
+}
+
+
+/* inlined code generation ****************************************************/
+
+static instruction * inline_instruction(inline_node *iln,
+ s4 opcode,
+ instruction *o_iptr)
+{
+ instruction *n_iptr;
+
+ n_iptr = (iln->inlined_iinstr_cursor++);
+ assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
+
+ n_iptr->opc = opcode;
+ n_iptr->flags.bits = o_iptr->flags.bits & INS_FLAG_ID_MASK;
+ n_iptr->line = o_iptr->line;
+
+ return n_iptr;
+}
+
+static void inline_generate_sync_builtin(inline_node *iln,
+ inline_node *callee,
+ instruction *o_iptr,
+ s4 instancevar,
+ functionptr func)
+{
+ int syncvar;
+ instruction *n_ins;
+
+ if (callee->m->flags & ACC_STATIC) {
+ /* ACONST */
+ syncvar = inline_new_temp_variable(iln->ctx->resultjd, TYPE_ADR);
+
+ n_ins = inline_instruction(iln, ICMD_ACONST, o_iptr);
+ n_ins->sx.val.c.cls = callee->m->clazz;
+ n_ins->dst.varindex = syncvar;
+ n_ins->flags.bits |= INS_FLAG_CLASS;
+ }
+ else {
+ syncvar = instancevar;
+ }
+
+ assert(syncvar != UNUSED);
+
+ /* MONITORENTER / MONITOREXIT */
+
+ n_ins = inline_instruction(iln, ICMD_BUILTIN, o_iptr);
+ n_ins->sx.s23.s3.bte = builtintable_get_internal(func);
+ n_ins->s1.argcount = 1; /* XXX add through-vars */
+ n_ins->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4));
+ n_ins->sx.s23.s2.args[0] = syncvar;
+}
+
+static s4 emit_inlining_prolog(inline_node *iln,
+ inline_node *callee,
+ instruction *o_iptr,
+ s4 *varmap)
+{
+ methodinfo *calleem;
+ methoddesc *md;
+ int i;
+ int localindex;
+ int type;
+ instruction *n_ins;
+ insinfo_inline *insinfo;
+ s4 varindex;
+
+ assert(iln && callee && o_iptr);
+
+ calleem = callee->m;
+ md = calleem->parseddesc;
+
+ /* INLINE_START instruction */
+
+ n_ins = inline_instruction(iln, ICMD_INLINE_START, o_iptr);
+
+ insinfo = (insinfo_inline*) DumpMemory::allocate(sizeof(insinfo_inline));
+ insinfo->method = callee->m;
+ insinfo->outer = iln->m;
+ insinfo->synclocal = callee->synclocal;
+ insinfo->synchronize = callee->synchronize;
+ insinfo->javalocals_start = NULL;
+ insinfo->javalocals_end = NULL;
+
+ /* info about stack vars live at the INLINE_START */
+
+ insinfo->throughcount = callee->n_passthroughcount;
+ insinfo->paramcount = md->paramcount;
+ insinfo->stackvarscount = o_iptr->s1.argcount;
+ insinfo->stackvars = (s4*) DumpMemory::allocate(sizeof(s4) * insinfo->stackvarscount);
+ for (i=0; i<insinfo->stackvarscount; ++i)
+ insinfo->stackvars[i] = iln->varmap[o_iptr->sx.s23.s2.args[i]];
+
+ /* info about the surrounding inlining */
+
+ if (iln->inline_start_instruction)
+ insinfo->parent = iln->inline_start_instruction->sx.s23.s3.inlineinfo;
+ else
+ insinfo->parent = NULL;
+
+ /* finish the INLINE_START instruction */
+
+ n_ins->sx.s23.s3.inlineinfo = insinfo;
+ callee->inline_start_instruction = n_ins;
+
+ DOLOG( printf("%sprolog: ", iln->indent);
+ show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); );
+
+ /* handle parameters for the inlined callee */
+
+ localindex = callee->localsoffset + md->paramslots;
+
+ for (i=md->paramcount-1; i>=0; --i) {
+ assert(iln);
+
+ type = md->paramtypes[i].type;
+
+ localindex -= IS_2_WORD_TYPE(type) ? 2 : 1;
+ assert(callee->regdata);
+
+ /* translate the argument variable */
+
+ varindex = varmap[o_iptr->sx.s23.s2.args[i]];
+ assert(varindex != UNUSED);
+
+ /* remove preallocation from the argument variable */
+
+ iln->ctx->resultjd->var[varindex].flags &= ~(PREALLOC | INMEMORY);
+
+ /* check the instance slot against NULL */
+ /* we don't need that for <init> methods, as the verifier */
+ /* ensures that they are only called for an uninit. object */
+ /* (which may not be NULL). */
+
+ if (!callee->isstatic && i == 0 && calleem->name != utf_init) {
+ assert(type == TYPE_ADR);
+ n_ins = inline_instruction(iln, ICMD_CHECKNULL, o_iptr);
+ n_ins->s1.varindex = varindex;
+ n_ins->dst.varindex = n_ins->s1.varindex;
+ }
+
+ /* store argument into local variable of inlined callee */
+
+ if (callee->jd->local_map[5*(localindex - callee->localsoffset) + type] != UNUSED)
+ {
+ /* this value is used in the callee */
+
+ if (i == 0 && callee->synclocal != UNUSED) {
+ /* we also need it for synchronization, so copy it */
+ assert(type == TYPE_ADR);
+ n_ins = inline_instruction(iln, ICMD_COPY, o_iptr);
+ }
+ else {
+ n_ins = inline_instruction(iln, ICMD_ISTORE + type, o_iptr);
+ n_ins->sx.s23.s3.javaindex = UNUSED;
+ }
+ n_ins->s1.varindex = varindex;
+ n_ins->dst.varindex = iln->ctx->resultjd->local_map[5*localindex + type];
+ assert(n_ins->dst.varindex != UNUSED);
+ }
+ else if (i == 0 && callee->synclocal != UNUSED) {
+ /* the value is not used inside the callee, but we need it for */
+ /* synchronization */
+ /* XXX In this case it actually makes no sense to create a */
+ /* separate synchronization variable. */
+
+ n_ins = inline_instruction(iln, ICMD_NOP, o_iptr);
+ }
+ else {
+ /* this value is not used, pop it */
+
+ n_ins = inline_instruction(iln, ICMD_POP, o_iptr);
+ n_ins->s1.varindex = varindex;
+ }
+
+ DOLOG( printf("%sprolog: ", iln->indent);
+ show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); );
+ }
+
+ /* COPY for synchronized instance methods */
+
+ if (callee->synclocal != UNUSED) {
+ n_ins = inline_instruction(iln, ICMD_COPY, o_iptr);
+ n_ins->s1.varindex = varmap[o_iptr->sx.s23.s2.args[0]];
+ n_ins->dst.varindex = callee->synclocal;
+
+ assert(n_ins->s1.varindex != UNUSED);
+ }
+
+ if (callee->synchronize) {
+ inline_generate_sync_builtin(iln, callee, o_iptr,
+ (callee->isstatic) ? UNUSED : varmap[o_iptr->sx.s23.s2.args[0]],
+ LOCK_monitor_enter);
+ }
+
+ /* INLINE_BODY instruction */
+
+ n_ins = inline_instruction(iln, ICMD_INLINE_BODY, callee->jd->basicblocks[0].iinstr);
+ n_ins->sx.s23.s3.inlineinfo = insinfo;
+
+ return 0; /* XXX */
+}
+
+
+static void emit_inlining_epilog(inline_node *iln, inline_node *callee, instruction *o_iptr)
+{
+ instruction *n_ins;
+ s4 *jl;
+
+ assert(iln && callee && o_iptr);
+ assert(callee->inline_start_instruction);
+
+ if (callee->synchronize) {
+ inline_generate_sync_builtin(iln, callee, o_iptr,
+ callee->synclocal,
+ LOCK_monitor_exit);
+ }
+
+ /* INLINE_END instruction */
+
+ n_ins = inline_instruction(iln, ICMD_INLINE_END, o_iptr);
+ n_ins->sx.s23.s3.inlineinfo = callee->inline_start_instruction->sx.s23.s3.inlineinfo;
+
+ /* set the javalocals */
+
+ jl = (s4*) DumpMemory::allocate(sizeof(s4) * iln->jd->maxlocals);
+ MCOPY(jl, iln->javalocals, s4, iln->jd->maxlocals);
+ n_ins->sx.s23.s3.inlineinfo->javalocals_end = jl;
+
+ DOLOG( printf("%sepilog: ", iln->indent);
+ show_icmd(iln->ctx->resultjd, n_ins, false, SHOW_STACK); printf("\n"); );
+}
+
+
+#define TRANSLATE_VAROP(vo) \
+ n_iptr->vo.varindex = inline_translate_variable(jd, origjd, varmap, n_iptr->vo.varindex)
+
+
+static void inline_clone_instruction(inline_node *iln,
+ jitdata *jd,
+ jitdata *origjd,
+ s4 *varmap,
+ instruction *o_iptr,
+ instruction *n_iptr)
+{
+ icmdtable_entry_t *icmdt;
+ builtintable_entry *bte;
+ methoddesc *md;
+ s4 i, j;
+ branch_target_t *table;
+ lookup_target_t *lookup;
+ inline_node *scope;
+
+ *n_iptr = *o_iptr;
+
+ icmdt = &(icmd_table[o_iptr->opc]);
+
+ switch (icmdt->dataflow) {
+ case DF_0_TO_0:
+ break;
+
+ case DF_3_TO_0:
+ TRANSLATE_VAROP(sx.s23.s3);
+ case DF_2_TO_0:
+ TRANSLATE_VAROP(sx.s23.s2);
+ case DF_1_TO_0:
+ TRANSLATE_VAROP(s1);
+ break;
+
+ case DF_2_TO_1:
+ TRANSLATE_VAROP(sx.s23.s2);
+ case DF_1_TO_1:
+ case DF_COPY:
+ case DF_MOVE:
+ TRANSLATE_VAROP(s1);
+ case DF_0_TO_1:
+ TRANSLATE_VAROP(dst);
+ break;
+
+ case DF_N_TO_1:
+ n_iptr->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4) * n_iptr->s1.argcount);
+ for (i=0; i<n_iptr->s1.argcount; ++i) {
+ n_iptr->sx.s23.s2.args[i] =
+ inline_translate_variable(jd, origjd, varmap,
+ o_iptr->sx.s23.s2.args[i]);
+ }
+ TRANSLATE_VAROP(dst);
+ break;
+
+ case DF_INVOKE:
+ INSTRUCTION_GET_METHODDESC(n_iptr, md);
+clone_call:
+ n_iptr->s1.argcount += iln->n_passthroughcount;
+ n_iptr->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4) * n_iptr->s1.argcount);
+ for (i=0; i<o_iptr->s1.argcount; ++i) {
+ n_iptr->sx.s23.s2.args[i] =
+ inline_translate_variable(jd, origjd, varmap,
+ o_iptr->sx.s23.s2.args[i]);
+ }
+ for (scope = iln; scope != NULL; scope = scope->parent) {
+ for (j = 0; j < scope->n_selfpassthroughcount; ++j) {
+ n_iptr->sx.s23.s2.args[i++] =
+ scope->parent->varmap[scope->n_passthroughvars[j]];
+ }
+ }
+ if (md->returntype.type != TYPE_VOID)
+ TRANSLATE_VAROP(dst);
+ break;
+
+ case DF_BUILTIN:
+ bte = n_iptr->sx.s23.s3.bte;
+ md = bte->md;
+ goto clone_call;
+
+ default:
+ assert(0);
+ }
+
+ switch (icmdt->controlflow) {
+ case CF_RET:
+ TRANSLATE_VAROP(s1); /* XXX should be handled by data-flow */
+ /* FALLTHROUGH */
+ case CF_IF:
+ case CF_GOTO:
+ inline_add_block_reference(iln, &(n_iptr->dst.block));
+ break;
+
+ case CF_JSR:
+ inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.jsrtarget.block));
+ break;
+
+ case CF_TABLE:
+ i = n_iptr->sx.s23.s3.tablehigh - n_iptr->sx.s23.s2.tablelow + 1 + 1 /* default */;
+
+ table = (branch_target_t*) DumpMemory::allocate(sizeof(branch_target_t) * i);
+ MCOPY(table, o_iptr->dst.table, branch_target_t, i);
+ n_iptr->dst.table = table;
+
+ while (--i >= 0) {
+ inline_add_block_reference(iln, &(table->block));
+ table++;
+ }
+ break;
+
+ case CF_LOOKUP:
+ inline_add_block_reference(iln, &(n_iptr->sx.s23.s3.lookupdefault.block));
+
+ i = n_iptr->sx.s23.s2.lookupcount;
+ lookup = (lookup_target_t*) DumpMemory::allocate(sizeof(lookup_target_t) * i);
+ MCOPY(lookup, o_iptr->dst.lookup, lookup_target_t, i);
+ n_iptr->dst.lookup = lookup;
+
+ while (--i >= 0) {
+ inline_add_block_reference(iln, &(lookup->target.block));
+ lookup++;
+ }
+ break;
+ }
+
+ /* XXX move this to dataflow section? */
+
+ switch (n_iptr->opc) {
+ case ICMD_ASTORE:
+#if 0
+ if (n_iptr->flags.bits & INS_FLAG_RETADDR)
+ inline_add_blocknr_reference(iln, &(n_iptr->sx.s23.s2.retaddrnr));
+#endif
+ /* FALLTHROUGH! */
+ case ICMD_ISTORE:
+ case ICMD_LSTORE:
+ case ICMD_FSTORE:
+ case ICMD_DSTORE:
+ stack_javalocals_store(n_iptr, iln->javalocals);
+ break;
+ }
+}
+
+
+static void inline_rewrite_method(inline_node *iln)
+{
+ basicblock *o_bptr;
+ s4 len;
+ instruction *o_iptr;
+ instruction *n_iptr;
+ inline_node *nextcall;
+ basicblock *n_bptr;
+ inline_block_map *bm;
+ int i;
+ int icount;
+ jitdata *resultjd;
+ jitdata *origjd;
+ char indent[100]; /* XXX debug */
+ s4 retcount;
+ s4 retidx;
+
+ assert(iln);
+
+ resultjd = iln->ctx->resultjd;
+ origjd = iln->jd;
+
+ n_bptr = NULL;
+ nextcall = iln->children;
+
+ /* XXX debug */
+ for (i=0; i<iln->depth; ++i)
+ indent[i] = '\t';
+ indent[i] = 0;
+ iln->indent = indent;
+
+ DOLOG( printf("%srewriting: ", indent); method_println(iln->m);
+ printf("%s(passthrough: %d+%d)\n",
+ indent, iln->n_passthroughcount - iln->n_selfpassthroughcount,
+ iln->n_passthroughcount); );
+
+ /* set memory cursors */
+
+ iln->inlined_iinstr_cursor = iln->inlined_iinstr;
+ iln->inlined_basicblocks_cursor = iln->inlined_basicblocks;
+
+ /* allocate temporary buffers */
+
+ iln->javalocals = (s4*) DumpMemory::allocate(sizeof(s4) * iln->jd->maxlocals);
+
+ /* loop over basic blocks */
+
+ o_bptr = iln->jd->basicblocks;
+ for (; o_bptr; o_bptr = o_bptr->next) {
+
+ if (o_bptr->flags < BBREACHED) {
+
+ /* ignore the dummy end block */
+
+ if (o_bptr->icount == 0 && o_bptr->next == NULL) {
+ /* enter the following block as translation, for exception handler ranges */
+ inline_block_translation(iln, o_bptr, iln->inlined_basicblocks_cursor);
+ continue;
+ }
+
+ DOLOG(
+ printf("%s* skipping old L%03d (flags=%d, type=%d, oid=%d) of ",
+ indent,
+ o_bptr->nr, o_bptr->flags, o_bptr->type,
+ o_bptr->indepth);
+ method_println(iln->m);
+ );
+
+ n_bptr = create_body_block(iln, o_bptr, iln->varmap);
+
+ /* enter it in the blockmap */
+
+ inline_block_translation(iln, o_bptr, n_bptr);
+
+ close_body_block(iln, n_bptr, o_bptr, iln->varmap, 0, -1);
+ continue;
+ }
+
+ len = o_bptr->icount;
+ o_iptr = o_bptr->iinstr;
+
+ DOLOG(
+ printf("%s* rewriting old L%03d (flags=%d, type=%d, oid=%d) of ",
+ indent,
+ o_bptr->nr, o_bptr->flags, o_bptr->type,
+ o_bptr->indepth);
+ method_println(iln->m);
+ show_basicblock(iln->jd, o_bptr, SHOW_STACK);
+ );
+
+ if (iln->blockbefore || o_bptr != iln->jd->basicblocks) {
+ /* create an inlined clone of this block */
+
+ n_bptr = create_body_block(iln, o_bptr, iln->varmap);
+ icount = 0;
+
+ /* enter it in the blockmap */
+
+ inline_block_translation(iln, o_bptr, n_bptr);
+
+ /* initialize the javalocals */
+
+ MCOPY(iln->javalocals, n_bptr->javalocals, s4, iln->jd->maxlocals);
+ }
+ else {
+ s4 *jl;
+
+ /* continue caller block */
+
+ n_bptr = iln->inlined_basicblocks_cursor - 1;
+ icount = n_bptr->icount;
+
+ /* translate the javalocals */
+
+ jl = translate_javalocals(iln, o_bptr->javalocals);
+ iln->inline_start_instruction->sx.s23.s3.inlineinfo->javalocals_start = jl;
+
+ MCOPY(iln->javalocals, jl, s4, iln->jd->maxlocals);
+ }
+
+ /* iterate over the ICMDs of this block */
+
+ retcount = 0;
+ retidx = UNUSED;
+
+ while (--len >= 0) {
+
+ DOLOG( fputs(indent, stdout); show_icmd(iln->jd, o_iptr, false, SHOW_STACK);
+ printf("\n") );
+
+ /* handle calls that will be inlined */
+
+ if (nextcall && o_iptr == nextcall->callerins) {
+
+ /* write the inlining prolog */
+
+ (void) emit_inlining_prolog(iln, nextcall, o_iptr, iln->varmap);
+ icount += nextcall->prolog_instructioncount;
+
+ /* end current block, or glue blocks together */
+
+ n_bptr->icount = icount;
+
+ if (nextcall->blockbefore) {
+ close_prolog_block(iln, n_bptr, nextcall);
+ }
+ else {
+ /* XXX */
+ }
+
+ /* check if the result is a local variable */
+
+ if (nextcall->m->parseddesc->returntype.type != TYPE_VOID
+ && o_iptr->dst.varindex < iln->jd->localcount)
+ {
+ nextcall->n_resultlocal = iln->varmap[o_iptr->dst.varindex];
+ }
+ else
+ nextcall->n_resultlocal = -1;
+
+ /* set memory pointers in the callee */
+
+ nextcall->inlined_iinstr = iln->inlined_iinstr_cursor;
+ nextcall->inlined_basicblocks = iln->inlined_basicblocks_cursor;
+
+ /* recurse */
+
+ DOLOG( printf("%sentering inline ", indent);
+ show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") );
+
+ inline_rewrite_method(nextcall);
+
+ DOLOG( printf("%sleaving inline ", indent);
+ show_icmd(origjd, o_iptr, false, SHOW_STACK); printf("\n") );
+
+ /* update memory cursors */
+
+ assert(nextcall->inlined_iinstr_cursor
+ <= iln->inlined_iinstr_cursor + nextcall->cumul_instructioncount);
+ assert(nextcall->inlined_basicblocks_cursor
+ == iln->inlined_basicblocks_cursor + nextcall->cumul_basicblockcount);
+ iln->inlined_iinstr_cursor = nextcall->inlined_iinstr_cursor;
+ iln->inlined_basicblocks_cursor = nextcall->inlined_basicblocks_cursor;
+
+ /* start new block, or glue blocks together */
+
+ if (nextcall->blockafter) {
+ n_bptr = create_epilog_block(iln, nextcall, iln->varmap);
+ icount = 0;
+ }
+ else {
+ n_bptr = iln->inlined_basicblocks_cursor - 1;
+ icount = n_bptr->icount;
+ /* XXX */
+ }
+
+ /* emit inlining epilog */
+
+ emit_inlining_epilog(iln, nextcall, o_iptr);
+ icount += nextcall->epilog_instructioncount;
+
+ /* proceed to next call */
+
+ nextcall = nextcall->next;
+ }
+ else {
+ n_iptr = (iln->inlined_iinstr_cursor++);
+ assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
+
+ switch (o_iptr->opc) {
+ case ICMD_RETURN:
+ if (iln->depth == 0)
+ goto default_clone;
+ goto return_tail;
+
+ case ICMD_IRETURN:
+ case ICMD_ARETURN:
+ case ICMD_LRETURN:
+ case ICMD_FRETURN:
+ case ICMD_DRETURN:
+ if (iln->depth == 0)
+ goto default_clone;
+ retcount = 1;
+ retidx = iln->varmap[o_iptr->s1.varindex];
+ if (iln->n_resultlocal != -1) {
+ /* store result in a local variable */
+
+ DOLOG( printf("USING RESULTLOCAL %d\n", iln->n_resultlocal); );
+ /* This relies on the same sequence of types for */
+ /* ?STORE and ?RETURN opcodes. */
+ n_iptr->opc = ICMD_ISTORE + (o_iptr->opc - ICMD_IRETURN);
+ n_iptr->s1.varindex = retidx;
+ n_iptr->dst.varindex = iln->n_resultlocal;
+ n_iptr->sx.s23.s3.javaindex = UNUSED; /* XXX set real javaindex? */
+
+ retcount = 0;
+ retidx = UNUSED;
+
+ n_iptr = (iln->inlined_iinstr_cursor++);
+ assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
+ icount++;
+ }
+ else if ((retidx < resultjd->localcount && iln->blockafter)
+ || !iln->blockafter) /* XXX do we really always need the MOVE? */
+ {
+ /* local must not become outvar, insert a MOVE */
+
+ n_iptr->opc = ICMD_MOVE;
+ n_iptr->s1.varindex = retidx;
+ retidx = inline_new_temp_variable(resultjd,
+ resultjd->var[retidx].type);
+ n_iptr->dst.varindex = retidx;
+
+ n_iptr = (iln->inlined_iinstr_cursor++);
+ assert((n_iptr - iln->inlined_iinstr) < iln->cumul_instructioncount);
+ icount++;
+ }
+return_tail:
+ if (iln->blockafter) {
+ n_iptr->opc = ICMD_GOTO;
+ n_iptr->dst.block = INLINE_RETURN_REFERENCE(iln);
+ inline_add_block_reference(iln, &(n_iptr->dst.block));
+ }
+ else {
+ n_iptr->opc = ICMD_NOP;
+ }
+ break;
+#if 0
+ if (o_bptr->next == NULL || (o_bptr->next->icount==0 && o_bptr->next->next == NULL)) {
+ n_iptr->opc = ICMD_NOP;
+ break;
+ }
+ goto default_clone;
+ break;
+#endif
+
+ default:
+default_clone:
+ inline_clone_instruction(iln, resultjd, iln->jd, iln->varmap, o_iptr, n_iptr);
+ }
+
+ DOLOG( fputs(indent, stdout); show_icmd(resultjd, n_iptr, false, SHOW_STACK);
+ printf("\n"););
+
+ icount++;
+ }
+
+ o_iptr++;
+ }
+
+ /* end of basic block */
+
+ if (iln->blockafter || (o_bptr->next && o_bptr->next->next)) {
+ close_body_block(iln, n_bptr, o_bptr, iln->varmap, retcount, retidx);
+ n_bptr->icount = icount;
+
+ DOLOG( printf("closed body block:\n");
+ show_basicblock(resultjd, n_bptr, SHOW_STACK); );
+ }
+ else {
+ n_bptr->icount = icount;
+ assert(iln->parent);
+ if (retidx != UNUSED)
+ iln->parent->varmap[iln->callerins->dst.varindex] = retidx;
+ }
+ }
+
+ bm = iln->ctx->blockmap;
+ for (i=0; i<iln->ctx->blockmap_index; ++i, ++bm) {
+ assert(bm->iln && bm->o_block && bm->n_block);
+ if (bm->iln == iln)
+ inline_resolve_block_refs(&(iln->refs), bm->o_block, bm->n_block, false);
+ }
+
+#if !defined(NDEBUG)
+ if (iln->refs) {
+ inline_target_ref *ref;
+ ref = iln->refs;
+ while (ref) {
+ if (!iln->depth || ref->isnumber || *(ref->ref.block) != INLINE_RETURN_REFERENCE(iln)) {
+ DOLOG( printf("XXX REMAINING REF at depth %d: %p\n", iln->depth,
+ (void*)*(ref->ref.block)) );
+ assert(false);
+ }
+ ref = ref->next;
+ }
+ }
+#endif
+}
+
+
+static exception_entry * inline_exception_tables(inline_node *iln,
+ exception_entry *n_extable,
+ exception_entry **prevextable)
+{
+ inline_node *child;
+ inline_node *scope;
+ exception_entry *et;
+
+ assert(iln);
+ assert(n_extable);
+ assert(prevextable);
+
+ child = iln->children;
+ if (child) {
+ do {
+ n_extable = inline_exception_tables(child, n_extable, prevextable);
+ child = child->next;
+ } while (child != iln->children);
+ }
+
+ et = iln->jd->exceptiontable;
+ for (; et != NULL; et = et->down) {
+ assert(et);
+ MZERO(n_extable, exception_entry, 1);
+ n_extable->start = inline_map_block(iln, et->start , iln);
+ n_extable->end = inline_map_block(iln, et->end , iln);
+ n_extable->handler = inline_map_block(iln, et->handler, iln);
+ n_extable->catchtype = et->catchtype;
+
+ if (*prevextable) {
+ (*prevextable)->down = n_extable;
+ }
+ *prevextable = n_extable;
+
+ n_extable++;
+ }
+
+ if (iln->handler_monitorexit) {
+ exception_entry **activehandlers;
+
+ MZERO(n_extable, exception_entry, 1);
+ n_extable->start = iln->inlined_basicblocks;
+ n_extable->end = iln->inlined_basicblocks_cursor;
+ n_extable->handler = iln->handler_monitorexit;
+ n_extable->catchtype.any = NULL; /* finally */
+
+ if (*prevextable) {
+ (*prevextable)->down = n_extable;
+ }
+ *prevextable = n_extable;
+
+ n_extable++;
+
+ /* We have to protect the created handler with the same handlers */
+ /* that protect the method body itself. */
+
+ for (scope = iln; scope->parent != NULL; scope = scope->parent) {
+
+ activehandlers = scope->o_handlers;
+ assert(activehandlers);
+
+ while (*activehandlers) {
+
+ assert(scope->parent);
+
+ MZERO(n_extable, exception_entry, 1);
+ n_extable->start = iln->handler_monitorexit;
+ n_extable->end = iln->handler_monitorexit + 1; /* XXX ok in this case? */
+ n_extable->handler = inline_map_block(scope->parent,
+ (*activehandlers)->handler,
+ scope->parent);
+ n_extable->catchtype = (*activehandlers)->catchtype;
+
+ if (*prevextable) {
+ (*prevextable)->down = n_extable;
+ }
+ *prevextable = n_extable;
+
+ n_extable++;
+ activehandlers++;
+ }
+ }
+ }
+
+ return n_extable;
+}
+
+
+static void inline_locals(inline_node *iln)
+{
+ inline_node *child;
+
+ assert(iln);
+
+ iln->varmap = create_variable_map(iln);
+
+ child = iln->children;
+ if (child) {
+ do {
+ inline_locals(child);
+ child = child->next;
+ } while (child != iln->children);
+ }
+
+ if (iln->regdata->memuse > iln->ctx->resultjd->rd->memuse)
+ iln->ctx->resultjd->rd->memuse = iln->regdata->memuse;
+ if (iln->regdata->argintreguse > iln->ctx->resultjd->rd->argintreguse)
+ iln->ctx->resultjd->rd->argintreguse = iln->regdata->argintreguse;
+ if (iln->regdata->argfltreguse > iln->ctx->resultjd->rd->argfltreguse)
+ iln->ctx->resultjd->rd->argfltreguse = iln->regdata->argfltreguse;
+}
+
+
+static void inline_interface_variables(inline_node *iln)
+{
+ basicblock *bptr;
+ jitdata *resultjd;
+ s4 i;
+ varinfo *v;
+
+ resultjd = iln->ctx->resultjd;
+
+ resultjd->interface_map = (interface_info*) DumpMemory::allocate(sizeof(interface_info) * 5 * iln->ctx->maxinoutdepth);
+ for (i=0; i<5*iln->ctx->maxinoutdepth; ++i)
+ resultjd->interface_map[i].flags = UNUSED;
+
+ for (bptr = resultjd->basicblocks; bptr != NULL; bptr = bptr->next) {
+ assert(bptr->indepth <= iln->ctx->maxinoutdepth);
+ assert(bptr->outdepth <= iln->ctx->maxinoutdepth);
+
+ for (i=0; i<bptr->indepth; ++i) {
+ v = &(resultjd->var[bptr->invars[i]]);
+ v->flags |= INOUT;
+ if (v->type == TYPE_RET)
+ v->flags |= PREALLOC;
+ else
+ v->flags &= ~PREALLOC;
+ v->flags &= ~INMEMORY;
+ assert(bptr->invars[i] >= resultjd->localcount);
+
+ if (resultjd->interface_map[5*i + v->type].flags == UNUSED) {
+ resultjd->interface_map[5*i + v->type].flags = v->flags;
+ }
+ else {
+ resultjd->interface_map[5*i + v->type].flags |= v->flags;
+ }
+ }
+
+ for (i=0; i<bptr->outdepth; ++i) {
+ v = &(resultjd->var[bptr->outvars[i]]);
+ v->flags |= INOUT;
+ if (v->type == TYPE_RET)
+ v->flags |= PREALLOC;
+ else
+ v->flags &= ~PREALLOC;
+ v->flags &= ~INMEMORY;
+ assert(bptr->outvars[i] >= resultjd->localcount);
+
+ if (resultjd->interface_map[5*i + v->type].flags == UNUSED) {
+ resultjd->interface_map[5*i + v->type].flags = v->flags;
+ }
+ else {
+ resultjd->interface_map[5*i + v->type].flags |= v->flags;
+ }
+ }
+ }
+}
+
+
+static void inline_write_exception_handlers(inline_node *master, inline_node *iln)
+{
+ basicblock *n_bptr;
+ instruction *n_ins;
+ inline_node *child;
+ builtintable_entry *bte;
+ s4 exvar;
+ s4 syncvar;
+ s4 i;
+
+ child = iln->children;
+ if (child) {
+ do {
+ inline_write_exception_handlers(master, child);
+ child = child->next;
+ } while (child != iln->children);
+ }
+
+ if (iln->synchronize) {
+ /* create the monitorexit handler */
+ n_bptr = create_block(master, iln, iln,
+ iln->n_passthroughcount + 1);
+ n_bptr->type = BBTYPE_EXH;
+ n_bptr->flags = BBFINISHED;
+
+ exvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0);
+ n_bptr->invars[iln->n_passthroughcount] = exvar;
+
+ iln->handler_monitorexit = n_bptr;
+
+ /* ACONST / ALOAD */
+
+ n_ins = master->inlined_iinstr_cursor++;
+ if (iln->m->flags & ACC_STATIC) {
+ n_ins->opc = ICMD_ACONST;
+ n_ins->sx.val.c.cls = iln->m->clazz;
+ n_ins->flags.bits = INS_FLAG_CLASS;
+ }
+ else {
+ n_ins->opc = ICMD_ALOAD;
+ n_ins->s1.varindex = iln->synclocal;
+ assert(n_ins->s1.varindex != UNUSED);
+ }
+ /* XXX could be PREALLOCed for builtin call */
+ syncvar = inline_new_variable(master->ctx->resultjd, TYPE_ADR, 0);
+ n_ins->dst.varindex = syncvar;
+ n_ins->line = 0;
+
+ /* MONITOREXIT */
+
+ bte = builtintable_get_internal(LOCK_monitor_exit);
+
+ n_ins = master->inlined_iinstr_cursor++;
+ n_ins->opc = ICMD_BUILTIN;
+ n_ins->s1.argcount = 1 + iln->n_passthroughcount + 1;
+ n_ins->sx.s23.s2.args = (s4*) DumpMemory::allocate(sizeof(s4) * n_ins->s1.argcount);
+ n_ins->sx.s23.s2.args[0] = syncvar;
+ for (i=0; i < iln->n_passthroughcount + 1; ++i) {
+ n_ins->sx.s23.s2.args[1 + i] = n_bptr->invars[i];
+ }
+ n_ins->sx.s23.s3.bte = bte;
+ n_ins->line = 0;
+
+ /* ATHROW */
+
+ n_ins = master->inlined_iinstr_cursor++;
+ n_ins->opc = ICMD_ATHROW;
+ n_ins->flags.bits = 0;
+ n_ins->s1.varindex = exvar;
+ n_ins->line = 0;
+
+ /* close basic block */
+
+ close_block(iln, iln, n_bptr, iln->n_passthroughcount);
+ n_bptr->icount = 3;
+ }
+}
+
+
+/* second pass driver *********************************************************/
+
+static bool inline_transform(inline_node *iln, jitdata *jd)
+{
+ instruction *n_ins;
+ basicblock *n_bb;
+ basicblock *n_bptr;
+ exception_entry *n_ext;
+ exception_entry *prevext;
+ codegendata *n_cd;
+ jitdata *n_jd;
+ s4 i;
+#if defined(INLINE_VERIFY_RESULT)
+ static int debug_verify_inlined_code = 1;
+#endif
+#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG)
+ static int debug_counter = 0;
+#endif
+
+ DOLOG( dump_inline_tree(iln, 0); );
+
+ assert(iln && jd);
+
+ n_ins = (instruction*) DumpMemory::allocate(sizeof(instruction) * iln->cumul_instructioncount);
+ MZERO(n_ins, instruction, iln->cumul_instructioncount);
+ iln->inlined_iinstr = n_ins;
+
+ n_bb = (basicblock*) DumpMemory::allocate(sizeof(basicblock) * iln->cumul_basicblockcount);
+ MZERO(n_bb, basicblock, iln->cumul_basicblockcount);
+ iln->inlined_basicblocks = n_bb;
+
+ iln->ctx->blockmap = (inline_block_map*) DumpMemory::allocate(sizeof(inline_block_map) * iln->cumul_blockmapcount);
+
+ n_jd = jit_jitdata_new(iln->m);
+ n_jd->flags = jd->flags;
+ n_jd->code->optlevel = jd->code->optlevel;
+ iln->ctx->resultjd = n_jd;
+
+ reg_setup(n_jd);
+
+ /* create the local_map */
+
+ n_jd->local_map = (s4*) DumpMemory::allocate(sizeof(s4) * 5 * iln->cumul_maxlocals);
+ for (i=0; i<5*iln->cumul_maxlocals; ++i)
+ n_jd->local_map[i] = UNUSED;
+
+ /* create / coalesce local variables */
+
+ n_jd->varcount = 0;
+ n_jd->vartop = 0;
+ n_jd->var = NULL;
+
+ inline_locals(iln);
+
+ n_jd->localcount = n_jd->vartop;
+
+ /* extra variables for verification (debugging) */
+
+#if defined(INLINE_VERIFY_RESULT)
+ if (debug_verify_inlined_code) {
+ n_jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + 100 /* XXX m->maxstack */;
+ if (n_jd->vartop > n_jd->varcount) {
+ /* XXX why? */
+ n_jd->var = (varinfo*) DumpMemory::realloc(n_jd->var, sizeof(varinfo) * n_jd->varcount, sizeof(varinfo) * n_jd->vartop);
+ n_jd->varcount = n_jd->vartop;
+ }
+ }
+#endif /* defined(INLINE_VERIFY_RESULT) */
+
+ /* write inlined code */
+
+ inline_rewrite_method(iln);
+
+ /* create exception handlers */
+
+ inline_write_exception_handlers(iln, iln);
+
+ /* write the dummy end block */
+
+ n_bptr = create_block(iln, iln, iln, 0);
+ n_bptr->flags = BBUNDEF;
+ n_bptr->type = BBTYPE_STD;
+
+ /* store created code in jitdata */
+
+ n_jd->basicblocks = iln->inlined_basicblocks;
+ n_jd->instructioncount = iln->cumul_instructioncount;
+ n_jd->instructions = iln->inlined_iinstr;
+
+ /* link the basic blocks (dummy end block is not counted) */
+
+ n_jd->basicblockcount = (iln->inlined_basicblocks_cursor - iln->inlined_basicblocks) - 1;
+ for (i=0; i<n_jd->basicblockcount + 1; ++i)
+ n_jd->basicblocks[i].next = &(n_jd->basicblocks[i+1]);
+ if (i)
+ n_jd->basicblocks[i-1].next = NULL;
+
+ /* check basicblock numbers */
+
+#if !defined(NDEBUG)
+ jit_check_basicblock_numbers(n_jd);
+#endif
+
+ /* create the exception table */
+
+ if (iln->cumul_exceptiontablelength) {
+ exception_entry *tableend;
+
+ n_ext = (exception_entry*) DumpMemory::allocate(sizeof(exception_entry) * iln->cumul_exceptiontablelength);
+ prevext = NULL;
+ tableend = inline_exception_tables(iln, n_ext, &prevext);
+ assert(tableend == n_ext + iln->cumul_exceptiontablelength);
+ if (prevext)
+ prevext->down = NULL;
+
+ n_jd->exceptiontablelength = iln->cumul_exceptiontablelength;
+ n_jd->exceptiontable = n_ext;
+ }
+ else {
+ n_ext = NULL;
+ }
+
+ /*******************************************************************************/
+
+ n_cd = n_jd->cd;
+ memcpy(n_cd, jd->cd, sizeof(codegendata));
+
+ n_cd->method = NULL; /* XXX */
+ n_jd->maxlocals = iln->cumul_maxlocals;
+ n_jd->maxinterfaces = iln->ctx->maxinoutdepth;
+
+ inline_post_process(n_jd);
+
+ inline_interface_variables(iln);
+
+ /* for debugging, verify the inlined result */
+
+#if defined(INLINE_VERIFY_RESULT)
+ if (debug_verify_inlined_code) {
+ debug_verify_inlined_code = 0;
+ DOLOG( printf("VERIFYING INLINED RESULT...\n"); fflush(stdout); );
+ if (!typecheck(n_jd)) {
+ exceptions_clear_exception();
+ DOLOG( printf("XXX INLINED RESULT DID NOT PASS VERIFIER XXX\n") );
+ return false;
+ }
+ else {
+ DOLOG( printf("VERIFICATION PASSED.\n") );
+ }
+ debug_verify_inlined_code = 1;
+ }
+#endif /* defined(INLINE_VERIFY_RESULT) */
+
+ /* we need bigger free memory stacks (XXX these should not be allocated in reg_setup) */
+
+ n_jd->rd->freemem = (s4*) DumpMemory::allocate(sizeof(s4) * (iln->ctx->maxinoutdepth + 1000)) /* XXX max vars/block */;
+
+#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG)
+ if ( (n_jd->instructioncount >= opt_InlineMinSize)
+ && (n_jd->instructioncount <= opt_InlineMaxSize))
+ {
+ if (debug_counter < opt_InlineCount)
+#endif /* defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG) */
+ {
+ /* install the inlined result */
+
+ *jd->code = *n_jd->code;
+ n_jd->code = jd->code;
+ *jd = *n_jd;
+
+ /* statistics and logging */
+
+#if !defined(NDEBUG)
+ inline_stat_roots++;
+
+ DOLOG_SHORT(
+ printf("==== %d.INLINE ==================================================================\n",
+ debug_counter);
+ printf("\ninline tree:\n");
+ dump_inline_tree(iln, 0);
+ n_jd->flags |= JITDATA_FLAG_SHOWINTERMEDIATE | JITDATA_FLAG_SHOWDISASSEMBLE;
+ /* debug_dump_inlined_code(iln, n_method, n_cd, n_rd); */
+ printf("-------- DONE -----------------------------------------------------------\n");
+ fflush(stdout);
+ );
+#endif
+ }
+
+#if defined(ENABLE_INLINING_DEBUG) || !defined(NDEBUG)
+ debug_counter++;
+ }
+#endif
+ return true;
+}
+
+
+/******************************************************************************/
+/* FIRST PASS: build inlining tree */
+/******************************************************************************/
+
+
+/* inline_pre_parse_heuristics *************************************************
+
+ Perform heuristic checks whether a call site should be inlined.
+ These checks are evaluated before the callee has been parsed and analysed.
+
+ IN:
+ caller...........inlining node of the caller
+ callee...........the called method
+ site.............information on the call site
+
+ RETURN VALUE:
+ true........consider for inlining
+ false.......don't inline
+
+*******************************************************************************/
+
+static bool inline_pre_parse_heuristics(const inline_node *caller,
+ const methodinfo *callee,
+ inline_site *site)
+{
+#if defined(INLINE_MAX_DEPTH)
+ if (caller->depth >= INLINE_MAX_DEPTH)
+ return false;
+#endif
+
+ return true;
+}
+
+
+/* inline_post_parse_heuristics ************************************************
+
+ Perform heuristic checks whether a call site should be inlined.
+ These checks are evaluated after the callee has been parsed and analysed.
+
+ IN:
+ caller...........inlining node of the caller (const)
+ callee...........the called method (const)
+
+ RETURN VALUE:
+ true........consider for inlining
+ false.......don't inline
+
+*******************************************************************************/
+
+static bool inline_post_parse_heuristics(const inline_node *caller,
+ const inline_node *callee)
+{
+ return true;
+}
+
+
+/* inline_afterwards_heuristics ************************************************
+
+ Perform heuristic checks whether a call site should be inlined.
+ These checks are evaluated after the inlining plan for the callee has
+ been made.
+
+ IN:
+ caller...........inlining node of the caller (const)
+ callee...........the called method (const)
+
+ RETURN VALUE:
+ true........consider for inlining
+ false.......don't inline
+
+*******************************************************************************/
+
+static bool inline_afterwards_heuristics(const inline_node *caller,
+ const inline_node *callee)
+{
+ inline_node *cumulator;
+
+#if defined(INLINE_DEPTH_FIRST)
+ cumulator = caller;
+#else
+ cumulator = caller->ctx->master;
+#endif
+
+ if (0
+#if defined(INLINE_MAX_BLOCK_EXPANSION)
+ || (cumulator->cumul_basicblockcount + callee->cumul_basicblockcount
+ > INLINE_MAX_BLOCK_EXPANSION*caller->ctx->master->jd->basicblockcount)
+#endif
+#if defined(INLINE_MAX_ICMD_EXPANSION)
+ || (cumulator->cumul_instructioncount + callee->cumul_instructioncount
+ > INLINE_MAX_ICMD_EXPANSION*caller->ctx->master->jd->instructioncount)
+#endif
+ )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+/* inline_is_monomorphic *******************************************************
+
+ Check if the given call site can be proven to be monomorphic.
+
+ IN:
+ callee...........the called method
+ call.............the invocation instruction
+
+ OUT:
+ site->speculative.....flags whether the inlining is speculative
+ (only defined if return value is true)
+
+ RETURN VALUE:
+ true if the call site is (currently) monomorphic,
+ false if not or unknown
+
+*******************************************************************************/
+
+static bool inline_is_monomorphic(const methodinfo *callee,
+ const instruction *call,
+ inline_site *site)
+{
+ if ((callee->flags & (ACC_STATIC | ACC_FINAL | ACC_PRIVATE)
+ || call->opc == ICMD_INVOKESPECIAL))
+ {
+ site->speculative = false;
+ return true;
+ }
+
+ /* XXX search single implementation for abstract monomorphics */
+
+ if ((callee->flags & (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED
+ | ACC_ABSTRACT))
+ == (ACC_METHOD_MONOMORPHIC | ACC_METHOD_IMPLEMENTED))
+ {
+ if (1) {
+ DOLOG( printf("SPECULATIVE INLINE: "); method_println((methodinfo*)callee); );
+ site->speculative = true;
+
+ return true;
+ }
+ }
+
+ /* possibly polymorphic call site */
+
+ return false;
+}
+
+
+/* inline_can_inline ***********************************************************
+
+ Check if inlining of the given call site is possible.
+
+ IN:
+ caller...........inlining node of the caller
+ callee...........the called method
+ call.............the invocation instruction
+
+ OUT:
+ site->speculative.....flags whether the inlining is speculative
+ (only defined if return value is true)
+
+ RETURN VALUE:
+ true if inlining is possible, false if not
+
+*******************************************************************************/
+
+static bool inline_can_inline(const inline_node *caller,
+ const methodinfo *callee,
+ const instruction *call,
+ inline_site *site)
+{
+ const inline_node *active;
+
+ /* cannot inline native methods */
+
+ if (callee->flags & ACC_NATIVE)
+ return false;
+
+ /* cannot inline possibly polymorphic calls */
+
+ if (!inline_is_monomorphic(callee, call, site))
+ return false;
+
+ /* cannot inline recursive calls */
+
+ for (active = caller; active; active = active->parent) {
+ if (callee == active->m) {
+ DOLOG( printf("RECURSIVE!\n") );
+ return false;
+ }
+ }
+
+ /* inlining is possible */
+
+ return true;
+}
+
+
+/* inline_create_callee_node ***************************************************
+
+ Create an inlining node for the given callee.
+
+ IN:
+ caller...........inlining node of the caller (const)
+ callee...........the called method
+
+ RETURN VALUE:
+ the new inlining node
+
+*******************************************************************************/
+
+static inline_node * inline_create_callee_node(const inline_node *caller,
+ methodinfo *callee)
+{
+ inline_node *cn; /* the callee inline_node */
+
+ cn = (inline_node*) DumpMemory::allocate(sizeof(inline_node));
+ MZERO(cn, inline_node, 1);
+
+ cn->depth = caller->depth + 1;
+ cn->ctx = caller->ctx;
+ cn->m = callee;
+ cn->synchronize = (callee->flags & ACC_SYNCHRONIZED);
+ cn->isstatic = (callee->flags & ACC_STATIC);
+
+ return cn;
+}
+
+
+/* inline_set_callee_properties ************************************************
+
+ Set properties of the inlined call site.
+
+ IN:
+ caller...........inlining node of the caller (const)
+ cn...............the called method
+ site.............info about the call site (const)
+
+ OUT:
+ *cn..............has the properties set
+
+*******************************************************************************/
+
+static void inline_set_callee_properties(const inline_node *caller,
+ inline_node *cn,
+ const inline_site *site)
+{
+ s4 argi;
+ s4 i, j;
+ basicblock *bptr;
+
+ /* set info about the call site */
+
+ cn->callerblock = site->bptr;
+ cn->callerins = site->iptr;
+ cn->callerpc = site->pc;
+ cn->o_handlers = site->handlers;
+ cn->n_handlercount = caller->n_handlercount + site->nhandlers;
+
+ /* determine if we need basic block boundaries before/after */
+
+ cn->blockbefore = false;
+ cn->blockafter = false;
+
+ if (cn->jd->branchtoentry)
+ cn->blockbefore = true;
+
+ if (cn->jd->branchtoend)
+ cn->blockafter = true;
+
+ if (cn->jd->returncount > 1)
+ cn->blockafter = true;
+
+ /* XXX make safer and reusable (maybe store last real block) */
+ for (bptr = cn->jd->basicblocks; bptr && bptr->next && bptr->next->next; bptr = bptr->next)
+ ;
+
+ if (cn->jd->returnblock != bptr)
+ cn->blockafter = true;
+
+ /* info about the callee */
+
+ cn->localsoffset = caller->localsoffset + caller->m->maxlocals;
+ cn->prolog_instructioncount = cn->m->parseddesc->paramcount + 2;
+ cn->epilog_instructioncount = 1; /* INLINE_END */
+ cn->extra_instructioncount = 0;
+
+ /* we need a CHECKNULL for instance methods, except for <init> */
+
+ if (!cn->isstatic && cn->m->name != utf_init)
+ cn->prolog_instructioncount += 1;
+
+ /* deal with synchronized callees */
+
+ if (cn->synchronize) {
+ methoddesc *md;
+ builtintable_entry *bte;
+
+ /* we need basic block boundaries because of the handler */
+
+ cn->blockbefore = true;
+ cn->blockafter = true;
+
+ /* for synchronized static methods */
+ /* we need an ACONST, MONITORENTER in the prolog */
+ /* and ACONST, MONITOREXIT in the epilog */
+
+ /* for synchronized instance methods */
+ /* we need an COPY, MONITORENTER in the prolog */
+ /* and MONITOREXIT in the epilog */
+
+ if (cn->isstatic) {
+ cn->prolog_instructioncount += 2;
+ cn->epilog_instructioncount += 2;
+ }
+ else {
+ cn->prolog_instructioncount += 2;
+ cn->epilog_instructioncount += 1;
+ cn->localsoffset += 1;
+ }
+
+ /* and exception handler */
+ /* ALOAD, builtin_monitorexit, ATHROW */
+
+ cn->extra_instructioncount += 3;
+
+ /* exception table entries */
+
+ cn->extra_exceptiontablelength = 1 + cn->n_handlercount;
+
+ /* add exception handler block */
+
+ cn->cumul_basicblockcount_root++;
+
+ /* we must call the builtins */
+
+ bte = builtintable_get_internal(LOCK_monitor_enter);
+ md = bte->md;
+ if (md->memuse > cn->regdata->memuse)
+ cn->regdata->memuse = md->memuse;
+ if (md->argintreguse > cn->regdata->argintreguse)
+ cn->regdata->argintreguse = md->argintreguse;
+
+ bte = builtintable_get_internal(LOCK_monitor_exit);
+ md = bte->md;
+ if (md->memuse > cn->regdata->memuse)
+ cn->regdata->memuse = md->memuse;
+ if (md->argintreguse > cn->regdata->argintreguse)
+ cn->regdata->argintreguse = md->argintreguse;
+ }
+
+ /* determine pass-through variables */
+
+ i = site->iptr->s1.argcount - cn->m->parseddesc->paramcount; /* max # of pass-though vars */
+
+ cn->n_passthroughvars = (s4*) DumpMemory::allocate(sizeof(s4) * i);
+ j = 0;
+ for (argi = site->iptr->s1.argcount - 1; argi >= cn->m->parseddesc->paramcount; --argi) {
+ s4 idx = site->iptr->sx.s23.s2.args[argi];
+ if (idx >= caller->jd->localcount) {
+ cn->n_passthroughvars[j] = idx;
+ j++;
+ }
+ else {
+ DOLOG( printf("PASSING THROUGH LOCAL VARIABLE %d\n", idx); );
+ }
+ }
+ assert(j <= i);
+ cn->n_selfpassthroughcount = j;
+ cn->n_passthroughcount = caller->n_passthroughcount + cn->n_selfpassthroughcount;
+}
+
+
+/* inline_cumulate_counters ****************************************************
+
+ Cumulate counters after a node has been decided to become inlined.
+
+ IN:
+ caller...........inlining node of the caller
+ callee...........inlining node of the callee (const)
+
+ OUT:
+ *caller..........gets cumulated values added
+
+*******************************************************************************/
+
+static void inline_cumulate_counters(inline_node *caller,
+ const inline_node *cn)
+{
+ caller->cumul_instructioncount += cn->prolog_instructioncount;
+ caller->cumul_instructioncount += cn->epilog_instructioncount;
+ caller->cumul_instructioncount += cn->extra_instructioncount;
+ caller->cumul_instructioncount += cn->cumul_instructioncount - 1 /*invoke*/;
+
+ caller->cumul_basicblockcount += cn->cumul_basicblockcount;
+ caller->cumul_basicblockcount_root += cn->cumul_basicblockcount_root;
+ caller->cumul_blockmapcount += cn->cumul_blockmapcount;
+ caller->cumul_exceptiontablelength += cn->cumul_exceptiontablelength;
+ caller->cumul_exceptiontablelength += cn->extra_exceptiontablelength;
+
+ if (cn->cumul_maxlocals > caller->cumul_maxlocals)
+ caller->cumul_maxlocals = cn->cumul_maxlocals;
+
+ /* XXX extra block after inlined call */
+ if (cn->blockafter) {
+ caller->cumul_basicblockcount += 1;
+ caller->cumul_blockmapcount += 1;
+ }
+}
+
+
+/* inline_analyse_callee *******************************************************
+
+ Analyse an inlining candidate callee.
+
+ IN:
+ caller...........inlining node of the caller
+ callee...........the called method
+ site.............info about the call site
+
+ OUT:
+ site->inlined....true if the callee has been selected for inlining
+
+ RETURN VALUE:
+ the inline node of the callee, or
+ NULL if an error has occurred (don't use the inlining plan in this case)
+
+*******************************************************************************/
+
+static inline_node * inline_analyse_callee(inline_node *caller,
+ methodinfo *callee,
+ inline_site *site)
+{
+ inline_node *cn; /* the callee inline_node */
+
+ /* create an inline tree node */
+
+ cn = inline_create_callee_node(caller, callee);
+
+ /* get the intermediate representation of the callee */
+
+ if (!inline_jit_compile(cn))
+ return NULL;
+
+ /* evaluate heuristics after parsing the callee */
+
+ if (!inline_post_parse_heuristics(caller, cn))
+ return cn;
+
+ /* the call site will be inlined */
+
+ site->inlined = true;
+
+ /* set info about the call site */
+
+ inline_set_callee_properties(caller, cn, site);
+
+ /* insert the node into the inline tree */
+
+ inline_insert_inline_node(caller, cn);
+
+ /* analyse recursively */
+
+ if (!inline_analyse_code(cn))
+ return NULL;
+
+ if (!inline_afterwards_heuristics(caller, cn)) {
+#if defined(INLINE_CANCEL_ON_THRESHOLD)
+ return NULL;
+#else
+ inline_remove_inline_node(caller, cn);
+ caller->ctx->stopped = true;
+ site->inlined = false;
+ return cn;
+#endif
+ }
+
+ /* cumulate counters */
+
+#if defined(INLINE_DEPTH_FIRST)
+ inline_cumulate_counters(caller, cn);
+#endif
+
+#if defined(INLINE_BREADTH_FIRST)
+ while (caller) {
+ inline_cumulate_counters(caller, cn);
+ caller = caller->parent;
+ }
+#endif
+
+ return cn;
+}
+
+
+/* inline_process_candidate ****************************************************
+
+ Process a selected inlining candidate.
+
+ IN:
+ cand.............the candidate
+
+ RETURN VALUE:
+ true........everything ok
+ false.......an error has occurred, don't use the plan
+
+*******************************************************************************/
+
+static bool inline_process_candidate(inline_candidate *cand)
+{
+ inline_node *cn;
+
+ cn = inline_analyse_callee(cand->caller,
+ cand->callee,
+ &(cand->site));
+
+ if (!cn)
+ return false;
+
+ if (!cand->site.inlined)
+ return true;
+
+ /* store assumptions */
+
+ if (cand->site.speculative)
+ method_add_assumption_monomorphic(cand->callee, cand->caller->ctx->master->m);
+
+ return true;
+}
+
+
+/* inline_analyse_code *********************************************************
+
+ Analyse the intermediate code of the given inlining node.
+
+ IN:
+ iln..............the inlining node
+
+ OUT:
+ *iln.............the inlining plan
+
+ RETURN VALUE:
+ true........everything ok
+ false.......an error has occurred, don't use the plan
+
+*******************************************************************************/
+
+static bool inline_analyse_code(inline_node *iln)
+{
+ methodinfo *m;
+ basicblock *bptr;
+ s4 len;
+ instruction *iptr;
+ methodinfo *callee;
+ exception_entry **handlers;
+ exception_entry *ex;
+ s4 nhandlers;
+ s4 blockendpc;
+ jitdata *mjd;
+ inline_site site;
+
+ assert(iln);
+
+ m = iln->m;
+ mjd = iln->jd;
+
+ /* initialize cumulative counters */
+
+ iln->cumul_maxlocals = iln->localsoffset + m->maxlocals;
+ iln->cumul_exceptiontablelength += mjd->exceptiontablelength;
+
+ /* iterate over basic blocks */
+
+ blockendpc = 0;
+
+ for (bptr = mjd->basicblocks; bptr; bptr = bptr->next) {
+
+ /* count the block */
+ /* ignore dummy end blocks (but count them for the blockmap) */
+
+ iln->cumul_blockmapcount++;
+ if ((bptr != mjd->basicblocks || iln->blockbefore)
+ &&
+ (bptr->icount > 0 || bptr->next != NULL))
+ iln->cumul_basicblockcount++;
+
+ /* skip dead code */
+
+ if (bptr->flags < BBREACHED)
+ continue;
+
+ /* allocate the buffer of active exception handlers */
+ /* XXX this wastes some memory, but probably it does not matter */
+
+ handlers = (exception_entry**) DumpMemory::allocate(sizeof(exception_entry*) * (mjd->exceptiontablelength + 1));
+
+ /* determine the active exception handlers for this block */
+ /* XXX maybe the handlers of a block should be part of our IR */
+ /* XXX this should share code with the type checkers */
+ nhandlers = 0;
+ for (ex = mjd->exceptiontable; ex ; ex = ex->down) {
+ if ((ex->start->nr <= bptr->nr) && (ex->end->nr > bptr->nr)) {
+ handlers[nhandlers++] = ex;
+ }
+ }
+ handlers[nhandlers] = NULL;
+
+ len = bptr->icount;
+ iptr = bptr->iinstr;
+
+ blockendpc += len;
+ iln->cumul_instructioncount += len;
+
+ /* iterate over the instructions of the block */
+
+ for (; --len >= 0; ++iptr) {
+
+ switch (iptr->opc) {
+ case ICMD_INVOKEVIRTUAL:
+ case ICMD_INVOKESPECIAL:
+ case ICMD_INVOKESTATIC:
+ case ICMD_INVOKEINTERFACE:
+
+ if (!INSTRUCTION_IS_UNRESOLVED(iptr) && !iln->ctx->stopped) {
+ callee = iptr->sx.s23.s3.fmiref->p.method;
+
+ if (inline_can_inline(iln, callee, iptr, &site)) {
+ site.inlined = false;
+ site.bptr = bptr;
+ site.iptr = iptr;
+ site.pc = blockendpc - len - 1;
+ site.handlers = handlers;
+ site.nhandlers = nhandlers;
+
+ if (inline_pre_parse_heuristics(iln, callee, &site)) {
+#if defined(INLINE_KNAPSACK) || defined(INLINE_BREADTH_FIRST)
+ inline_add_candidate(iln->ctx, iln, callee, &site);
+#else
+ inline_candidate cand;
+ cand.caller = iln;
+ cand.callee = callee;
+ cand.site = site;
+
+ if (!inline_process_candidate(&cand))
+ return false;
+#endif
+ }
+ }
+ }
+ break;
+
+ case ICMD_RETURN:
+ case ICMD_IRETURN:
+ case ICMD_ARETURN:
+ case ICMD_LRETURN:
+ case ICMD_FRETURN:
+ case ICMD_DRETURN:
+ /* extra ICMD_MOVE may be necessary */
+ iln->cumul_instructioncount++;
+ break;
+ }
+ }
+
+ /* end of basic block */
+ }
+
+ return true;
+}
+
+
+static void inline_cumulate_counters_recursive(inline_node *iln)
+{
+ inline_node *child;
+
+ child = iln->children;
+ if (child) {
+ do {
+ inline_cumulate_counters_recursive(child);
+ inline_cumulate_counters(iln, child);
+ child = child->next;
+ } while (child != iln->children);
+ }
+}
+
+
+/* inline_make_inlining_plan ***************************************************
+
+ Make an inlining plan for the given root node
+
+ IN:
+ iln..............the root node
+
+ OUT:
+ *iln.............the inlining plan
+
+ RETURN VALUE:
+ true........everything ok
+ false.......an error has occurred, don't use the plan
+
+*******************************************************************************/
+
+#if defined(INLINE_KNAPSACK)
+static bool inline_make_inlining_plan(inline_node *iln)
+{
+ inline_candidate *cand;
+#if defined(INLINE_COST_BUDGET)
+ s4 budget = INLINE_COST_BUDGET;
+# define BUDGETMEMBER cost
+#endif
+#if defined(INLINE_WEIGHT_BUDGET)
+ double budget = INLINE_WEIGHT_BUDGET;
+# define BUDGETMEMBER weight
+#endif
+
+ inline_analyse_code(iln);
+
+ DOLOG( printf("candidates in "); method_println(iln->m);
+ inline_candidates_println(iln->ctx); );
+
+ while ((cand = inline_pick_best_candidate(iln->ctx)) != NULL)
+ {
+ if (cand->BUDGETMEMBER <= budget) {
+ DOLOG( printf(" picking: "); inline_candidate_println(cand); );
+
+ if (!inline_process_candidate(cand))
+ return false;
+
+#if !defined(INLINE_ADD_NEGATIVE_TO_BUDGET)
+ if (cand->BUDGETMEMBER > 0)
+#endif
+ budget -= cand->BUDGETMEMBER;
+ }
+ }
+
+ inline_cumulate_counters_recursive(iln);
+
+ return true;
+}
+#endif /* defined(INLINE_KNAPSACK) */
+
+
+#if defined(INLINE_DEPTH_FIRST)
+static bool inline_make_inlining_plan(inline_node *iln)
+{
+ return inline_analyse_code(iln);
+}
+#endif /* defined(INLINE_DEPTH_FIRST) */
+
+
+#if defined(INLINE_BREADTH_FIRST)
+static bool inline_make_inlining_plan(inline_node *iln)
+{
+ inline_candidate *cand;
+
+ inline_analyse_code(iln);
+
+ DOLOG( printf("candidates in "); method_println(iln->m);
+ inline_candidates_println(iln->ctx); );
+
+ while (!iln->ctx->stopped
+ && (cand = inline_pick_best_candidate(iln->ctx)) != NULL)
+ {
+ DOLOG( printf(" picking: "); inline_candidate_println(cand); );
+
+ if (!inline_process_candidate(cand))
+ return false;
+ }
+
+ return true;
+}
+#endif /* defined(INLINE_BREADTH_FIRST) */
+
+
+/* statistics *****************************************************************/
+
+#if defined(INLINE_STATISTICS)
+static void inline_gather_statistics_recursive(inline_node *iln)
+{
+ inline_node *child;
+
+ inline_stat_inlined_nodes++;
+
+ if (iln->depth > inline_stat_max_depth)
+ inline_stat_max_depth++;
+
+ child = iln->children;
+ if (child) {
+ do {
+ inline_gather_statistics_recursive(child);
+ child = child->next;
+ } while (child != iln->children);
+ }
+}
+#endif /* defined(INLINE_STATISTICS) */
+
+
+#if defined(INLINE_STATISTICS)
+static void inline_gather_statistics(inline_node *iln)
+{
+ inline_stat_roots_transformed++;
+
+ inline_gather_statistics_recursive(iln);
+}
+#endif /* defined(INLINE_STATISTICS) */
+
+
+/* post processing ************************************************************/
+
+#define POSTPROCESS_SRC(varindex) live[varindex]--
+#define POSTPROCESS_DST(varindex) live[varindex]++
+
+#define POSTPROCESS_SRCOP(s) POSTPROCESS_SRC(iptr->s.varindex)
+#define POSTPROCESS_DSTOP(d) POSTPROCESS_DST(iptr->d.varindex)
+
+#define MARKSAVED(varindex) jd->var[varindex].flags |= SAVEDVAR
+
+#define MARK_ALL_SAVED \
+ do { \
+ for (i=0; i<jd->vartop; ++i) \
+ if (live[i]) \
+ MARKSAVED(i); \
+ } while (0)
+
+static void inline_post_process(jitdata *jd)
+{
+ codeinfo *code;
+ basicblock *bptr;
+ instruction *iptr;
+ instruction *iend;
+ s4 i;
+ icmdtable_entry_t *icmdt;
+ s4 *live;
+ methoddesc *md;
+ builtintable_entry *bte;
+
+ /* Get required compiler data. */
+
+ code = jd->code;
+
+ /* reset the SAVEDVAR flag of all variables */
+
+ for (i=0; i<jd->vartop; ++i)
+ jd->var[i].flags &= ~SAVEDVAR;
+
+ /* allocate the life counters */
+
+ live = (s4*) DumpMemory::allocate(sizeof(s4) * jd->vartop);
+ MZERO(live, s4, jd->vartop);
+
+ /* iterate over all basic blocks */
+
+ for (bptr = jd->basicblocks; bptr != NULL; bptr = bptr->next) {
+ if (bptr->flags < BBREACHED)
+ continue;
+
+ /* make invars live */
+
+ for (i=0; i<bptr->indepth; ++i)
+ POSTPROCESS_DST(bptr->invars[i]);
+
+ iptr = bptr->iinstr;
+ iend = iptr + bptr->icount;
+
+ for (; iptr < iend; ++iptr) {
+
+ icmdt = &(icmd_table[iptr->opc]);
+
+ switch (icmdt->dataflow) {
+ case DF_3_TO_0:
+ POSTPROCESS_SRCOP(sx.s23.s3);
+ case DF_2_TO_0:
+ POSTPROCESS_SRCOP(sx.s23.s2);
+ case DF_1_TO_0:
+ POSTPROCESS_SRCOP(s1);
+ case DF_0_TO_0:
+ if (icmdt->flags & ICMDTABLE_CALLS) {
+ code_unflag_leafmethod(code);
+ MARK_ALL_SAVED;
+ }
+ break;
+
+ case DF_2_TO_1:
+ POSTPROCESS_SRCOP(sx.s23.s2);
+ case DF_1_TO_1:
+ case DF_MOVE:
+ POSTPROCESS_SRCOP(s1);
+ case DF_0_TO_1:
+ if (icmdt->flags & ICMDTABLE_CALLS) {
+ code_unflag_leafmethod(code);
+ MARK_ALL_SAVED;
+ }
+ case DF_COPY:
+ POSTPROCESS_DSTOP(dst);
+ break;
+
+ case DF_N_TO_1:
+ for (i=0; i<iptr->s1.argcount; ++i) {
+ POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]);
+ }
+ if (icmdt->flags & ICMDTABLE_CALLS) {
+ code_unflag_leafmethod(code);
+ MARK_ALL_SAVED;
+ }
+ POSTPROCESS_DSTOP(dst);
+ break;
+
+ case DF_INVOKE:
+ INSTRUCTION_GET_METHODDESC(iptr, md);
+ post_process_call:
+ code_unflag_leafmethod(code);
+ for (i=0; i<md->paramcount; ++i) {
+ POSTPROCESS_SRC(iptr->sx.s23.s2.args[i]);
+ }
+ for (; i<iptr->s1.argcount; ++i) {
+ MARKSAVED(iptr->sx.s23.s2.args[i]);
+ }
+ if (md->returntype.type != TYPE_VOID)
+ POSTPROCESS_DSTOP(dst);
+ break;
+
+ case DF_BUILTIN:
+ bte = iptr->sx.s23.s3.bte;
+ md = bte->md;
+ goto post_process_call;
+
+ default:
+ assert(0);
+ }
+
+ } /* end instruction loop */
+
+ /* consume outvars */
+
+ for (i=0; i<bptr->outdepth; ++i)
+ POSTPROCESS_SRC(bptr->outvars[i]);
+
+#if !defined(NDEBUG)
+ for (i=jd->localcount; i < jd->vartop; ++i)
+ assert(live[i] == 0);
+#endif
+
+ } /* end basic block loop */
+}
+
+
+/* inline_create_root_node *****************************************************
+
+ Create the root node of the inlining tree.
+
+ IN:
+ jd...............the current jitdata of the root method
+
+ RETURN VALUE:
+ the root node of the inlining tree
+
+*******************************************************************************/
+
+static inline_node * inline_create_root_node(jitdata *jd)
+{
+ inline_node *iln;
+
+ iln = (inline_node*) DumpMemory::allocate(sizeof(inline_node));
+ MZERO(iln, inline_node, 1);
+
+ iln->m = jd->m;
+ iln->jd = jd;
+ iln->regdata = jd->rd;
+
+ iln->blockbefore = true;
+ iln->blockafter = true;
+
+ iln->cumul_instructioncount = 0;
+ iln->cumul_basicblockcount = 1 /* dummy end block */;
+
+ /* create inlining context */
+
+ iln->ctx = (inline_context*) DumpMemory::allocate(sizeof(inline_context));
+ MZERO(iln->ctx, inline_context, 1);
+ iln->ctx->master = iln;
+ iln->ctx->next_debugnr = 1; /* XXX debug */
+
+ return iln;
+}
+
+
+/******************************************************************************/
+/* MAIN DRIVER FUNCTION */
+/******************************************************************************/
+
+bool inline_inline(jitdata *jd)
+{
+ inline_node *iln;
+
+ DOLOG( printf("==== INLINE ==================================================================\n");
+ show_method(jd, SHOW_STACK); );
+
+#if defined(INLINE_STATISTICS)
+ inline_stat_roots++;
+#endif
+
+ iln = inline_create_root_node(jd);
+
+ if (inline_make_inlining_plan(iln)) {
+
+ /* add blocks to the root node */
+
+ iln->cumul_basicblockcount += iln->cumul_basicblockcount_root;
+ iln->cumul_blockmapcount += iln->cumul_basicblockcount_root;
+
+ DOLOG( printf("==== INLINE TRANSFORM ========================================================\n"); );
+
+ if (iln->children)
+ inline_transform(iln, jd);
+
+#if defined(INLINE_STATISTICS)
+ inline_gather_statistics(iln);
+#endif
+ }
+
+ DOLOG( printf("-------- DONE -----------------------------------------------------------\n");
+ fflush(stdout); );
+
+ return true;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/inline/inline.h - code inliner
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _INLINE_H
-#define _INLINE_H
-
-#include "config.h"
-
-#include <stdbool.h>
-
-#include "vm/jit/jit.hpp"
-
-
-/* function prototypes ********************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-bool inline_inline(jitdata *jd);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _INLINE_H */
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/inline/inline.h - code inliner
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _INLINE_H
+#define _INLINE_H
+
+#include "config.h"
+
+#include <stdbool.h>
+
+#include "vm/jit/jit.hpp"
+
+
+/* function prototypes ********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool inline_inline(jitdata *jd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _INLINE_H */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
codegen.c \
codegen.h \
disasm.c \
- disass.c \
+ disass.cpp \
dynamic-super.c \
engine1.c \
engine2.c \
#include "vm/jit/builtin.hpp"
#include "vm/class.hpp"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/loader.hpp"
#include "vm/options.h"
#include "vm/jit/codegen-common.hpp"
#include "vm/jit/dseg.h"
#include "vm/jit/jit.hpp"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher.h"
#include "vm/jit/stack.h"
#include "vm/jit/stacktrace.hpp"
+++ /dev/null
-/* src/vm/jit/intrp/disass.c - disassembler wrapper for interpreter
-
- Copyright (C) 1996-2005, 2006, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <stdio.h>
-
-#include "vm/types.h"
-
-#include "vm/jit/intrp/intrp.h"
-
-
-/* function disassinstr ********************************************************
-
- outputs a disassembler listing of one machine code instruction on 'stdout'
- c: instructions machine code
-
-*******************************************************************************/
-
-u1 *intrp_disassinstr(u1 *code)
-{
- FILE *savedout;
- u1 *r;
-
- savedout = vm_out;
- vm_out = stdout;
- r = (u1 *) vm_disassemble_inst((Inst *) code, vm_prim);
- vm_out = savedout;
-
- return r;
-}
-
-
-/* function disassemble ********************************************************
-
- outputs a disassembler listing of some machine code on 'stdout'
- code: pointer to first instruction
- len: code size (number of instructions * 4)
-
-*******************************************************************************/
-
-void intrp_disassemble(u1 *start, u1 *end)
-{
- FILE *savedout;
-
- printf(" --- disassembler listing ---\n");
- savedout = vm_out;
- vm_out = stdout;
- vm_disassemble((Inst *) start, (Inst *) end, vm_prim);
- vm_out = savedout;
-}
-
-
-void printarg_ui (u4 ui )
-{
- fprintf(vm_out, "%ud", ui);
-}
-
-void printarg_v (Cell v )
-{
- fprintf(vm_out, "%lld", (long long)v);
-}
-
-void printarg_Cell (Cell x )
-{
- fprintf(vm_out, "%lld", (long long)x);
-}
-
-
-void printarg_b (s4 b )
-{
- fprintf(vm_out, "%d", b);
-}
-
-void printarg_s (s4 s )
-{
- fprintf(vm_out, "%d", s);
-}
-
-void printarg_i (s4 i )
-{
- fprintf(vm_out, "%d", i);
-}
-
-void printarg_l (s8 l )
-{
- fprintf(vm_out, "%lld", (long long)l);
-}
-
-void printarg_f (float f )
-{
- fprintf(vm_out, "%f", (double)f);
-}
-
-void printarg_d (double d )
-{
- fprintf(vm_out, "%f", d);
-}
-
-void printarg_aRef (java_objectheader *aRef )
-{
- fprintf(vm_out, "obj: %p", (void *)aRef);
-}
-
-void printarg_aArray (java_arrayheader * aArray )
-{
- fprintf(vm_out, "array %p", (void *)aArray);
-}
-
-void printarg_aaTarget(Inst ** aaTarget)
-{
- if (aaTarget) {
- methodinfo *m=((methodinfo **)aaTarget)[3];
- printarg_am(m);
- } else
- fprintf(vm_out, "NULL");
-}
-
-void printarg_aClass (classinfo * aClass )
-{
- if (aClass)
- utf_fprint_printable_ascii_classname(vm_out, aClass->name);
- else
- fprintf(vm_out, "NULL");
-}
-
-void printarg_acr (constant_classref *acr )
-{
- fprintf(vm_out, "cr: %p", (void *)acr);
-}
-
-void printarg_addr (u1 * addr )
-{
- fprintf(vm_out, "%p", (void *)addr);
-}
-
-void printarg_af (functionptr af )
-{
- fprintf(vm_out, "f: %p", (void *)af);
-}
-
-void printarg_afi (fieldinfo * afi )
-{
- if (afi) {
- utf_fprint_printable_ascii_classname(vm_out, afi->clazz->name);
- fprintf(vm_out, ".");
- utf_fprint_printable_ascii(vm_out, afi->name);
- utf_fprint_printable_ascii(vm_out, afi->descriptor);
- } else
- fprintf(vm_out, "fi=NULL");
-}
-
-void printarg_am (methodinfo * am )
-{
- if (am) {
- utf_fprint_printable_ascii_classname(vm_out, am->clazz->name);
- fprintf(vm_out, ".");
- utf_fprint_printable_ascii(vm_out, am->name);
- utf_fprint_printable_ascii(vm_out, am->descriptor);
- } else
- fprintf(vm_out, "m=NULL");
-}
-
-void printarg_acell (Cell * acell )
-{
- fprintf(vm_out, "%p", (void *)acell);
-}
-
-void printarg_ainst (Inst * ainst )
-{
- fprintf(vm_out, "%p", (void *)ainst);
-}
-
-void printarg_auf (unresolved_field * auf )
-{
- if (auf) {
- utf_fprint_printable_ascii(vm_out, auf->fieldref->name);
- fprintf(vm_out, " (type ");
- utf_fprint_printable_ascii(vm_out, auf->fieldref->descriptor);
- fprintf(vm_out, ")");
- } else
- fprintf(vm_out, "NULL");
-}
-
-void printarg_aum (unresolved_method *aum )
-{
- if (aum) {
- utf_fprint_printable_ascii_classname(vm_out, METHODREF_CLASSNAME(aum->methodref));
- fprintf(vm_out, ".");
- utf_fprint_printable_ascii(vm_out, aum->methodref->name);
- utf_fprint_printable_ascii(vm_out, aum->methodref->descriptor);
- } else
- fprintf(vm_out, "NULL");
-}
-
-void printarg_avftbl (vftbl_t * avftbl )
-{
- if (avftbl) {
- fprintf(vm_out, "vftbl: ");
- utf_fprint_printable_ascii_classname(vm_out, avftbl->class->name);
- } else
- fprintf(vm_out, "NULL");
-}
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/intrp/disass.c - disassembler wrapper for interpreter
+
+ Copyright (C) 1996-2005, 2006, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include "vm/types.h"
+
+#include "vm/jit/intrp/intrp.h"
+
+
+/* function disassinstr ********************************************************
+
+ outputs a disassembler listing of one machine code instruction on 'stdout'
+ c: instructions machine code
+
+*******************************************************************************/
+
+u1 *intrp_disassinstr(u1 *code)
+{
+ FILE *savedout;
+ u1 *r;
+
+ savedout = vm_out;
+ vm_out = stdout;
+ r = (u1 *) vm_disassemble_inst((Inst *) code, vm_prim);
+ vm_out = savedout;
+
+ return r;
+}
+
+
+/* function disassemble ********************************************************
+
+ outputs a disassembler listing of some machine code on 'stdout'
+ code: pointer to first instruction
+ len: code size (number of instructions * 4)
+
+*******************************************************************************/
+
+void intrp_disassemble(u1 *start, u1 *end)
+{
+ FILE *savedout;
+
+ printf(" --- disassembler listing ---\n");
+ savedout = vm_out;
+ vm_out = stdout;
+ vm_disassemble((Inst *) start, (Inst *) end, vm_prim);
+ vm_out = savedout;
+}
+
+
+void printarg_ui (u4 ui )
+{
+ fprintf(vm_out, "%ud", ui);
+}
+
+void printarg_v (Cell v )
+{
+ fprintf(vm_out, "%lld", (long long)v);
+}
+
+void printarg_Cell (Cell x )
+{
+ fprintf(vm_out, "%lld", (long long)x);
+}
+
+
+void printarg_b (s4 b )
+{
+ fprintf(vm_out, "%d", b);
+}
+
+void printarg_s (s4 s )
+{
+ fprintf(vm_out, "%d", s);
+}
+
+void printarg_i (s4 i )
+{
+ fprintf(vm_out, "%d", i);
+}
+
+void printarg_l (s8 l )
+{
+ fprintf(vm_out, "%lld", (long long)l);
+}
+
+void printarg_f (float f )
+{
+ fprintf(vm_out, "%f", (double)f);
+}
+
+void printarg_d (double d )
+{
+ fprintf(vm_out, "%f", d);
+}
+
+void printarg_aRef (java_objectheader *aRef )
+{
+ fprintf(vm_out, "obj: %p", (void *)aRef);
+}
+
+void printarg_aArray (java_arrayheader * aArray )
+{
+ fprintf(vm_out, "array %p", (void *)aArray);
+}
+
+void printarg_aaTarget(Inst ** aaTarget)
+{
+ if (aaTarget) {
+ methodinfo *m=((methodinfo **)aaTarget)[3];
+ printarg_am(m);
+ } else
+ fprintf(vm_out, "NULL");
+}
+
+void printarg_aClass (classinfo * aClass )
+{
+ if (aClass)
+ utf_fprint_printable_ascii_classname(vm_out, aClass->name);
+ else
+ fprintf(vm_out, "NULL");
+}
+
+void printarg_acr (constant_classref *acr )
+{
+ fprintf(vm_out, "cr: %p", (void *)acr);
+}
+
+void printarg_addr (u1 * addr )
+{
+ fprintf(vm_out, "%p", (void *)addr);
+}
+
+void printarg_af (functionptr af )
+{
+ fprintf(vm_out, "f: %p", (void *)af);
+}
+
+void printarg_afi (fieldinfo * afi )
+{
+ if (afi) {
+ utf_fprint_printable_ascii_classname(vm_out, afi->clazz->name);
+ fprintf(vm_out, ".");
+ utf_fprint_printable_ascii(vm_out, afi->name);
+ utf_fprint_printable_ascii(vm_out, afi->descriptor);
+ } else
+ fprintf(vm_out, "fi=NULL");
+}
+
+void printarg_am (methodinfo * am )
+{
+ if (am) {
+ utf_fprint_printable_ascii_classname(vm_out, am->clazz->name);
+ fprintf(vm_out, ".");
+ utf_fprint_printable_ascii(vm_out, am->name);
+ utf_fprint_printable_ascii(vm_out, am->descriptor);
+ } else
+ fprintf(vm_out, "m=NULL");
+}
+
+void printarg_acell (Cell * acell )
+{
+ fprintf(vm_out, "%p", (void *)acell);
+}
+
+void printarg_ainst (Inst * ainst )
+{
+ fprintf(vm_out, "%p", (void *)ainst);
+}
+
+void printarg_auf (unresolved_field * auf )
+{
+ if (auf) {
+ utf_fprint_printable_ascii(vm_out, auf->fieldref->name);
+ fprintf(vm_out, " (type ");
+ utf_fprint_printable_ascii(vm_out, auf->fieldref->descriptor);
+ fprintf(vm_out, ")");
+ } else
+ fprintf(vm_out, "NULL");
+}
+
+void printarg_aum (unresolved_method *aum )
+{
+ if (aum) {
+ utf_fprint_printable_ascii_classname(vm_out, METHODREF_CLASSNAME(aum->methodref));
+ fprintf(vm_out, ".");
+ utf_fprint_printable_ascii(vm_out, aum->methodref->name);
+ utf_fprint_printable_ascii(vm_out, aum->methodref->descriptor);
+ } else
+ fprintf(vm_out, "NULL");
+}
+
+void printarg_avftbl (vftbl_t * avftbl )
+{
+ if (avftbl) {
+ fprintf(vm_out, "vftbl: ");
+ utf_fprint_printable_ascii_classname(vm_out, avftbl->class->name);
+ } else
+ fprintf(vm_out, "NULL");
+}
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
#include "vm/class.hpp"
#include "vm/global.h"
-#include "vm/linker.h"
-#include "vm/method.h"
+#include "vm/linker.hpp"
+#include "vm/method.hpp"
#include "vm/references.h"
#include "vm/resolve.hpp"
#include <stdint.h>
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/jit/ir/instruction.hpp"
#include <stdint.h>
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/replace.hpp"
#include "vm/globals.hpp"
#include "vm/initialize.hpp"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/rt-timing.h"
#include "vm/statistics.h"
#include "vm/jit/disass.h"
#include "vm/jit/dseg.h"
#include "vm/jit/jit.hpp"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/show.hpp"
#endif
#if defined(ENABLE_INLINING)
-# include "vm/jit/inline/inline.h"
+# include "vm/jit/inline/inline.hpp"
#endif
#include "vm/jit/ir/bytecode.h"
# include "vm/jit/python.h"
#endif
-#include "vm/jit/verify/typecheck.h"
+#include "vm/jit/verify/typecheck.hpp"
/* debug macros ***************************************************************/
#include "vm/types.h"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/references.h"
#include "vm/resolve.hpp"
#include "vm/jit/stacktrace.hpp"
#if defined(ENABLE_INLINING)
-# include "vm/jit/inline/inline.h"
+# include "vm/jit/inline/inline.hpp"
#endif
#include "vm/jit/ir/bytecode.h"
# include "vm/jit/allocator/lsra.h"
#endif
-#include "vm/jit/verify/typeinfo.h"
+#include "vm/jit/verify/typeinfo.hpp"
/* common jit/codegen macros **************************************************/
#include "toolbox/list.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/code.hpp"
#include "config.h"
#include "vm/types.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/loop/loop.h"
#include "vm/types.h"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/emit-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/abi.h"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "vm/jit/stacktrace.hpp"
M_CMPLT(s1, s2, REG_ITMP3);
M_CMPLT(s2, s1, REG_ITMP1);
M_ISUB(REG_ITMP1, REG_ITMP3, d);
- M_BNEZ(d, 4);
- M_NOP;
+ emit_label_bnez(cd, BRANCH_LABEL_1, d);
s1 = emit_load_s1_low(jd, iptr, REG_ITMP1);
s2 = emit_load_s2_low(jd, iptr, REG_ITMP2);
M_CMPULT(s1, s2, REG_ITMP3);
M_CMPULT(s2, s1, REG_ITMP1);
M_ISUB(REG_ITMP1, REG_ITMP3, d);
+ emit_label(cd, BRANCH_LABEL_1);
#endif
emit_store_dst(jd, iptr, d);
break;
ICONST(REG_ITMP2, iptr->sx.val.l >> 32);
M_CMPLT(s1, REG_ITMP2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2);
s2 = emit_load_s1_low(jd, iptr, REG_ITMP3);
- M_BNE(s1, REG_ITMP2, 5); /* XXX */
- M_NOP;
ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff);
M_CMPULT(s2, REG_ITMP2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
}
#endif
break;
#else
if (iptr->sx.val.l == 0) {
s1 = emit_load_s1(jd, iptr, REG_ITMP12_PACKED);
- M_BGTZ(GET_HIGH_REG(s1), 5); /* XXX */
- M_NOP;
+ emit_label_bgtz(cd, BRANCH_LABEL_1, GET_HIGH_REG(s1));
emit_bltz(cd, iptr->dst.block, GET_HIGH_REG(s1));
- emit_beqz(cd, iptr->dst.block, GET_LOW_REG(s1));
+ emit_beqz(cd, iptr->dst.block, GET_LOW_REG(s1));
+ emit_label(cd, BRANCH_LABEL_1);
}
else {
s1 = emit_load_s1_high(jd, iptr, REG_ITMP1);
ICONST(REG_ITMP2, iptr->sx.val.l >> 32);
M_CMPLT(s1, REG_ITMP2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2);
s2 = emit_load_s1_low(jd, iptr, REG_ITMP3);
- M_BNE(s1, REG_ITMP2, 5); /* XXX */
- M_NOP;
ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff);
M_CMPUGT(s2, REG_ITMP2, REG_ITMP3);
emit_beqz(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
}
#endif
break;
if (iptr->sx.val.l == 0) {
s1 = emit_load_s1(jd, iptr, REG_ITMP12_PACKED);
emit_bgtz(cd, iptr->dst.block, GET_HIGH_REG(s1));
- M_BLTZ(GET_HIGH_REG(s1), 3); /* XXX */
- M_NOP;
+ emit_label_bltz(cd, BRANCH_LABEL_1, GET_HIGH_REG(s1));
emit_bnez(cd, iptr->dst.block, GET_LOW_REG(s1));
+ emit_label(cd, BRANCH_LABEL_1);
}
else {
s1 = emit_load_s1_high(jd, iptr, REG_ITMP1);
ICONST(REG_ITMP2, iptr->sx.val.l >> 32);
M_CMPGT(s1, REG_ITMP2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2);
s2 = emit_load_s1_low(jd, iptr, REG_ITMP3);
- M_BNE(s1, REG_ITMP2, 5); /* XXX */
- M_NOP;
ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff);
M_CMPUGT(s2, REG_ITMP2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
}
#endif
break;
ICONST(REG_ITMP2, iptr->sx.val.l >> 32);
M_CMPGT(s1, REG_ITMP2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label_bne(cd, BRANCH_LABEL_1, s1, REG_ITMP2);
s2 = emit_load_s1_low(jd, iptr, REG_ITMP3);
- M_BNE(s1, REG_ITMP2, 5); /* XXX */
- M_NOP;
ICONST(REG_ITMP2, iptr->sx.val.l & 0xffffffff);
M_CMPULT(s2, REG_ITMP2, REG_ITMP3);
emit_beqz(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
}
#endif
break;
s1 = emit_load_s1_high(jd, iptr, REG_ITMP1);
s2 = emit_load_s2_high(jd, iptr, REG_ITMP2);
- M_BNE(s1, s2, 3); /* XXX TWISTI: uff, that is a problem */
- M_NOP;
+ emit_label_bne(cd, BRANCH_LABEL_1, s1, s2);
s1 = emit_load_s1_low(jd, iptr, REG_ITMP1);
s2 = emit_load_s2_low(jd, iptr, REG_ITMP2);
emit_beq(cd, iptr->dst.block, s1, s2);
+ emit_label(cd, BRANCH_LABEL_1);
break;
#endif
M_CMPLT(s1, s2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
M_CMPGT(s1, s2, REG_ITMP3);
- /* load low-bits before the branch, so we know the distance */
+ emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3);
s1 = emit_load_s1_low(jd, iptr, REG_ITMP1);
s2 = emit_load_s2_low(jd, iptr, REG_ITMP2);
- M_BNEZ(REG_ITMP3, 4); /* XXX */
- M_NOP;
M_CMPULT(s1, s2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
break;
#endif
M_CMPGT(s1, s2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
M_CMPLT(s1, s2, REG_ITMP3);
- /* load low-bits before the branch, so we know the distance */
+ emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3);
s1 = emit_load_s1_low(jd, iptr, REG_ITMP1);
s2 = emit_load_s2_low(jd, iptr, REG_ITMP2);
- M_BNEZ(REG_ITMP3, 4); /* XXX */
- M_NOP;
M_CMPUGT(s1, s2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
break;
#endif
M_CMPLT(s1, s2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
M_CMPGT(s1, s2, REG_ITMP3);
- /* load low-bits before the branch, so we know the distance */
+ emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3);
s1 = emit_load_s1_low(jd, iptr, REG_ITMP1);
s2 = emit_load_s2_low(jd, iptr, REG_ITMP2);
- M_BNEZ(REG_ITMP3, 4); /* XXX */
- M_NOP;
M_CMPUGT(s1, s2, REG_ITMP3);
emit_beqz(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
break;
#endif
M_CMPGT(s1, s2, REG_ITMP3);
emit_bnez(cd, iptr->dst.block, REG_ITMP3);
M_CMPLT(s1, s2, REG_ITMP3);
- /* load low-bits before the branch, so we know the distance */
+ emit_label_bnez(cd, BRANCH_LABEL_1, REG_ITMP3);
s1 = emit_load_s1_low(jd, iptr, REG_ITMP1);
s2 = emit_load_s2_low(jd, iptr, REG_ITMP2);
- M_BNEZ(REG_ITMP3, 4); /* XXX */
- M_NOP;
M_CMPULT(s1, s2, REG_ITMP3);
emit_beqz(cd, iptr->dst.block, REG_ITMP3);
+ emit_label(cd, BRANCH_LABEL_1);
break;
#endif
#include "mm/memory.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/abi.h"
#include "vm/jit/stack.h"
extern "C" {
-#include "vm/method.h"
+#include "vm/method.hpp"
#include <opagent.h>
}
#include "toolbox/bitvector.h"
#include "vm/class.hpp"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/references.h"
#include "vm/resolve.hpp"
#include "config.h"
#include "vm/class.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/optimizing/escape.h"
#define _VM_JIT_OPTIMIZING_ESCAPE_H
#include "vm/jit/jit.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
typedef enum {
ESCAPE_UNKNOWN,
#include "vm/types.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/vm.hpp"
#include "vm/jit/codegen-common.hpp"
#include "vm/statistics.h"
#include "vm/options.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/abi.h"
#include "vm/jit/reg.h"
#include "vm/jit/builtin.hpp"
#include "vm/class.hpp"
-#include "vm/classcache.h"
-#include "vm/method.h"
+#include "vm/classcache.hpp"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/string.hpp"
#include "threads/mutex.hpp"
#include "threads/thread.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/exceptions.hpp"
#include "vm/options.h"
#include "vm/string.hpp"
#include "threads/condition.hpp"
#include "threads/mutex.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#ifdef __cplusplus
+++ /dev/null
-/* src/vm/jit/parse.c - parser for JavaVM to intermediate code translation
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <string.h>
-
-#include "vm/types.h"
-
-#include "mm/memory.h"
-
-#include "native/native.hpp"
-
-#include "threads/lock.hpp"
-
-#include "toolbox/logging.h"
-
-#include "vm/jit/builtin.hpp"
-#include "vm/exceptions.hpp"
-#include "vm/global.h"
-#include "vm/linker.h"
-#include "vm/loader.hpp"
-#include "vm/options.h"
-#include "vm/resolve.hpp"
-
-#if defined(ENABLE_STATISTICS)
-# include "vm/statistics.h"
-#endif
-
-#include "vm/string.hpp"
-#include "vm/suck.hpp"
-
-#include "vm/jit/asmpart.h"
-#include "vm/jit/jit.hpp"
-#include "vm/jit/parse.h"
-#include "vm/jit/loop/loop.h"
-
-#include "vm/jit/ir/bytecode.h"
-
-
-#define INSTRUCTIONS_INCREMENT 5 /* number of additional instructions to */
- /* allocate if space runs out */
-
-
-/* local macros ***************************************************************/
-
-#define BYTECODEINDEX_TO_BASICBLOCK(dst) \
- do { \
- (dst).block = \
- parse_bytecodeindex_to_basicblock(jd, &pd, (dst).insindex); \
- } while (0)
-
-
-/* parserdata_t ***************************************************************/
-
-typedef struct parsedata_t parsedata_t;
-
-struct parsedata_t {
- u1 *bytecodestart; /* start of bytecode instructions */
- u1 *basicblockstart; /* start of bytecode basic-blocks */
-
- s4 *bytecodemap; /* bytecode to IR mapping */
-
- instruction *instructions; /* instruction array */
- s4 instructionslength; /* length of the instruction array */
-
- s4 *instructionmap; /* IR to basic-block mapping */
-};
-
-
-/* parse_setup *****************************************************************
-
- Fills the passed parsedata_t structure.
-
-*******************************************************************************/
-
-static void parse_setup(jitdata *jd, parsedata_t *pd)
-{
- methodinfo *m;
-
- /* get required compiler data */
-
- m = jd->m;
-
- /* bytecode start array */
-
- pd->bytecodestart = DMNEW(u1, m->jcodelength + 1);
- MZERO(pd->bytecodestart, u1, m->jcodelength + 1);
-
- /* bytecode basic-block start array */
-
- pd->basicblockstart = DMNEW(u1, m->jcodelength + 1);
- MZERO(pd->basicblockstart, u1, m->jcodelength + 1);
-
- /* bytecode instruction index to IR instruction mapping */
-
- pd->bytecodemap = DMNEW(s4, m->jcodelength + 1);
- MSET(pd->bytecodemap, -1, s4, m->jcodelength + 1);
-
- /* allocate the instruction array */
-
- pd->instructionslength = m->jcodelength + 1;
- pd->instructions = DMNEW(instruction, pd->instructionslength);
-
- /* Zero the intermediate instructions array so we don't have any
- invalid pointers in it if we cannot finish stack_analyse(). */
-
- MZERO(pd->instructions, instruction, pd->instructionslength);
-
- /* The instructionmap is allocated later when we know the count of
- instructions. */
-
- pd->instructionmap = NULL;
-}
-
-
-/* parse_realloc_instructions **************************************************
-
- Reallocate the instructions array so there is room for at least N
- additional instructions.
-
- RETURN VALUE:
- the new value for iptr
-
-*******************************************************************************/
-
-static instruction *parse_realloc_instructions(parsedata_t *pd, s4 icount, s4 n)
-{
- /* increase the size of the instruction array */
-
- pd->instructionslength += (n + INSTRUCTIONS_INCREMENT);
-
- /* reallocate the array */
-
- pd->instructions = DMREALLOC(pd->instructions, instruction, icount,
- pd->instructionslength);
- MZERO(pd->instructions + icount, instruction,
- (pd->instructionslength - icount));
-
- /* return the iptr */
-
- return pd->instructions + icount;
-}
-
-
-/* parse_bytecodeindex_to_basicblock *******************************************
-
- Resolves a bytecode index to the corresponding basic block.
-
-*******************************************************************************/
-
-static basicblock *parse_bytecodeindex_to_basicblock(jitdata *jd,
- parsedata_t *pd,
- s4 bcindex)
-{
- s4 irindex;
- basicblock *bb;
-
- irindex = pd->bytecodemap[bcindex];
- bb = jd->basicblocks + pd->instructionmap[irindex];
-
- return bb;
-}
-
-
-/* parse_mark_exception_boundaries *********************************************
-
- Mark exception handlers and the boundaries of the handled regions as
- basic block boundaries.
-
- IN:
- jd...............current jitdata
-
- RETURN VALUE:
- true.............everything ok
- false............an exception has been thrown
-
-*******************************************************************************/
-
-static bool parse_mark_exception_boundaries(jitdata *jd, parsedata_t *pd)
-{
- s4 bcindex;
- s4 i;
- s4 len;
- raw_exception_entry *rex;
- methodinfo *m;
-
- m = jd->m;
-
- len = m->rawexceptiontablelength;
-
- if (len == 0)
- return true;
-
- rex = m->rawexceptiontable;
-
- for (i = 0; i < len; ++i, ++rex) {
-
- /* the start of the handled region becomes a basic block start */
-
- bcindex = rex->startpc;
- CHECK_BYTECODE_INDEX(bcindex);
- MARK_BASICBLOCK(pd, bcindex);
-
- bcindex = rex->endpc; /* see JVM Spec 4.7.3 */
- CHECK_BYTECODE_INDEX_EXCLUSIVE(bcindex);
-
- /* check that the range is valid */
-
-#if defined(ENABLE_VERIFIER)
- if (bcindex <= rex->startpc) {
- exceptions_throw_verifyerror(m, "Invalid exception handler range");
- return false;
- }
-#endif
-
- /* End of handled region becomes a basic block boundary (if it
- is the bytecode end, we'll use the special end block that
- is created anyway). */
-
- if (bcindex < m->jcodelength)
- MARK_BASICBLOCK(pd, bcindex);
- else
- jd->branchtoend = true;
-
- /* the start of the handler becomes a basic block start */
-
- bcindex = rex->handlerpc;
- CHECK_BYTECODE_INDEX(bcindex);
- MARK_BASICBLOCK(pd, bcindex);
- }
-
- /* everything ok */
-
- return true;
-
-#if defined(ENABLE_VERIFIER)
-throw_invalid_bytecode_index:
- exceptions_throw_verifyerror(m,
- "Illegal bytecode index in exception table");
- return false;
-#endif
-}
-
-
-/* parse_resolve_exception_table ***********************************************
-
- Enter the exception handlers and their ranges, resolved to basicblock *s,
- in the jitdata.
-
- IN:
- jd...............current jitdata
-
- RETURN VALUE:
- true.............everything ok
- false............an exception has been thrown
-
-*******************************************************************************/
-
-static bool parse_resolve_exception_table(jitdata *jd, parsedata_t *pd)
-{
- methodinfo *m;
- raw_exception_entry *rex;
- exception_entry *ex;
- s4 i;
- s4 len;
- classinfo *exclass;
-
- m = jd->m;
-
- len = m->rawexceptiontablelength;
-
- /* common case: no handler entries */
-
- if (len == 0)
- return true;
-
- /* allocate the exception table */
-
- jd->exceptiontablelength = len;
- jd->exceptiontable = DMNEW(exception_entry, len + 1); /* XXX why +1? */
-
- /* copy and resolve the entries */
-
- ex = jd->exceptiontable;
- rex = m->rawexceptiontable;
-
- for (i = 0; i < len; ++i, ++rex, ++ex) {
- /* resolve instruction indices to basic blocks */
-
- ex->start = parse_bytecodeindex_to_basicblock(jd, pd, rex->startpc);
- ex->end = parse_bytecodeindex_to_basicblock(jd, pd, rex->endpc);
- ex->handler = parse_bytecodeindex_to_basicblock(jd, pd, rex->handlerpc);
-
- /* lazily resolve the catchtype */
-
- if (rex->catchtype.any != NULL) {
- if (!resolve_classref_or_classinfo(m,
- rex->catchtype,
- resolveLazy, true, false,
- &exclass))
- return false;
-
- /* if resolved, enter the result of resolution in the table */
-
- if (exclass != NULL)
- rex->catchtype.cls = exclass;
- }
-
- ex->catchtype = rex->catchtype;
- ex->next = NULL; /* set by loop analysis */
- ex->down = ex + 1; /* link to next exception entry */
- }
-
- /* terminate the ->down linked list */
-
- assert(ex != jd->exceptiontable);
- ex[-1].down = NULL;
-
- return true;
-}
-
-
-/*******************************************************************************
-
- function 'parse' scans the JavaVM code and generates intermediate code
-
- During parsing the block index table is used to store at bit pos 0
- a flag which marks basic block starts and at position 1 to 31 the
- intermediate instruction index. After parsing the block index table
- is scanned, for marked positions a block is generated and the block
- number is stored in the block index table.
-
-*******************************************************************************/
-
-/*** macro for checking the length of the bytecode ***/
-
-#if defined(ENABLE_VERIFIER)
-#define CHECK_END_OF_BYTECODE(neededlength) \
- do { \
- if ((neededlength) > m->jcodelength) \
- goto throw_unexpected_end_of_bytecode; \
- } while (0)
-#else /* !ENABLE_VERIFIER */
-#define CHECK_END_OF_BYTECODE(neededlength)
-#endif /* ENABLE_VERIFIER */
-
-bool parse(jitdata *jd)
-{
- methodinfo *m; /* method being parsed */
- codeinfo *code;
- parsedata_t pd;
- instruction *iptr; /* current ptr into instruction array */
-
- s4 bcindex; /* bytecode instruction index */
- s4 nextbc; /* start of next bytecode instruction */
- s4 opcode; /* bytecode instruction opcode */
-
- s4 irindex; /* IR instruction index */
- s4 ircount; /* IR instruction count */
-
- s4 bbcount; /* basic block count */
-
- int s_count = 0; /* stack element counter */
- bool blockend; /* true if basic block end has been reached */
- bool iswide; /* true if last instruction was a wide */
-
- constant_classref *cr;
- constant_classref *compr;
- classinfo *c;
- builtintable_entry *bte;
- constant_FMIref *fmi;
- methoddesc *md;
- unresolved_method *um;
- unresolved_field *uf;
-
- resolve_result_t result;
- u2 lineindex = 0;
- u2 currentline = 0;
- u2 linepcchange = 0;
- u4 flags;
- basicblock *bptr;
-
- int *local_map; /* local pointer to renaming map */
- /* is assigned to rd->local_map at the end */
- branch_target_t *table;
- lookup_target_t *lookup;
- s4 i;
- s4 j;
-
- /* get required compiler data */
-
- m = jd->m;
- code = jd->code;
-
- /* allocate buffers for local variable renaming */
-
- local_map = DMNEW(int, m->maxlocals * 5);
-
- for (i = 0; i < m->maxlocals; i++) {
- local_map[i * 5 + 0] = 0;
- local_map[i * 5 + 1] = 0;
- local_map[i * 5 + 2] = 0;
- local_map[i * 5 + 3] = 0;
- local_map[i * 5 + 4] = 0;
- }
-
- /* initialize the parse data structures */
-
- parse_setup(jd, &pd);
-
- /* initialize local variables */
-
- iptr = pd.instructions;
- ircount = 0;
- bbcount = 0;
- blockend = false;
- iswide = false;
-
- /* mark basic block boundaries for exception table */
-
- if (!parse_mark_exception_boundaries(jd, &pd))
- return false;
-
- /* initialize stack element counter */
-
- s_count = 1 + m->rawexceptiontablelength;
-
- /* setup line number info */
-
- currentline = 0;
- linepcchange = 0;
-
- if (m->linenumbercount == 0) {
- lineindex = 0;
- }
- else {
- linepcchange = m->linenumbers[0].start_pc;
- }
-
- /*** LOOP OVER ALL BYTECODE INSTRUCTIONS **********************************/
-
- for (bcindex = 0; bcindex < m->jcodelength; bcindex = nextbc) {
-
- /* mark this position as a valid bytecode instruction start */
-
- pd.bytecodestart[bcindex] = 1;
-
- /* change the current line number, if necessary */
-
- /* XXX rewrite this using pointer arithmetic */
-
- if (linepcchange == bcindex) {
- if (m->linenumbercount > lineindex) {
-next_linenumber:
- currentline = m->linenumbers[lineindex].line_number;
- lineindex++;
- if (lineindex < m->linenumbercount) {
- linepcchange = m->linenumbers[lineindex].start_pc;
- if (linepcchange == bcindex)
- goto next_linenumber;
- }
- }
- }
-
-fetch_opcode:
- /* fetch next opcode */
-
- opcode = SUCK_BE_U1(m->jcode + bcindex);
-
- /* If the previous instruction was a block-end instruction,
- mark the current bytecode instruction as basic-block
- starting instruction. */
-
- /* NOTE: Some compilers put a BC_nop after a blockend
- instruction. */
-
- if (blockend && (opcode != BC_nop)) {
- MARK_BASICBLOCK(&pd, bcindex);
- blockend = false;
- }
-
- /* If the current bytecode instruction was marked as
- basic-block starting instruction before (e.g. blockend,
- forward-branch target), mark the current IR instruction
- too. */
-
- if (pd.basicblockstart[bcindex] != 0) {
- /* We need a NOP as last instruction in each basic block
- for basic block reordering (may be replaced with a GOTO
- later). */
-
- INSTRUCTIONS_CHECK(1);
- OP(ICMD_NOP);
- }
-
- /* store intermediate instruction count (bit 0 mark block starts) */
-
- pd.bytecodemap[bcindex] = ircount;
-
- /* compute next instruction start */
-
- nextbc = bcindex + bytecode[opcode].length;
-
- CHECK_END_OF_BYTECODE(nextbc);
-
- /* add stack elements produced by this instruction */
-
- s_count += bytecode[opcode].slots;
-
- /* We check here for the space of 1 instruction in the
- instruction array. If an opcode is converted to more than
- 1 instruction, this is checked in the corresponding
- case. */
-
- INSTRUCTIONS_CHECK(1);
-
- /* translate this bytecode instruction */
- switch (opcode) {
-
- case BC_nop:
- break;
-
- /* pushing constants onto the stack ***********************************/
-
- case BC_bipush:
- OP_LOADCONST_I(SUCK_BE_S1(m->jcode + bcindex + 1));
- break;
-
- case BC_sipush:
- OP_LOADCONST_I(SUCK_BE_S2(m->jcode + bcindex + 1));
- break;
-
- case BC_ldc1:
- i = SUCK_BE_U1(m->jcode + bcindex + 1);
- goto pushconstantitem;
-
- case BC_ldc2:
- case BC_ldc2w:
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
-
- pushconstantitem:
-
-#if defined(ENABLE_VERIFIER)
- if (i >= m->clazz->cpcount) {
- exceptions_throw_verifyerror(m,
- "Attempt to access constant outside range");
- return false;
- }
-#endif
-
- switch (m->clazz->cptags[i]) {
- case CONSTANT_Integer:
- OP_LOADCONST_I(((constant_integer *) (m->clazz->cpinfos[i]))->value);
- break;
- case CONSTANT_Long:
- OP_LOADCONST_L(((constant_long *) (m->clazz->cpinfos[i]))->value);
- break;
- case CONSTANT_Float:
- OP_LOADCONST_F(((constant_float *) (m->clazz->cpinfos[i]))->value);
- break;
- case CONSTANT_Double:
- OP_LOADCONST_D(((constant_double *) (m->clazz->cpinfos[i]))->value);
- break;
- case CONSTANT_String:
- OP_LOADCONST_STRING(literalstring_new((utf *) (m->clazz->cpinfos[i])));
- break;
- case CONSTANT_Class:
- cr = (constant_classref *) (m->clazz->cpinfos[i]);
-
- if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
- return false;
-
- /* if not resolved, c == NULL */
-
- OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr);
-
- break;
-
-#if defined(ENABLE_VERIFIER)
- default:
- exceptions_throw_verifyerror(m,
- "Invalid constant type to push");
- return false;
-#endif
- }
- break;
-
- case BC_aconst_null:
- OP_LOADCONST_NULL();
- break;
-
- case BC_iconst_m1:
- case BC_iconst_0:
- case BC_iconst_1:
- case BC_iconst_2:
- case BC_iconst_3:
- case BC_iconst_4:
- case BC_iconst_5:
- OP_LOADCONST_I(opcode - BC_iconst_0);
- break;
-
- case BC_lconst_0:
- case BC_lconst_1:
- OP_LOADCONST_L(opcode - BC_lconst_0);
- break;
-
- case BC_fconst_0:
- case BC_fconst_1:
- case BC_fconst_2:
- OP_LOADCONST_F(opcode - BC_fconst_0);
- break;
-
- case BC_dconst_0:
- case BC_dconst_1:
- OP_LOADCONST_D(opcode - BC_dconst_0);
- break;
-
- /* stack operations ***************************************************/
-
- /* We need space for additional instruction so we can
- translate these instructions to sequences of ICMD_COPY and
- ICMD_MOVE instructions. */
-
- case BC_dup_x1:
- INSTRUCTIONS_CHECK(4);
- OP(opcode);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- break;
-
- case BC_dup_x2:
- INSTRUCTIONS_CHECK(6);
- OP(opcode);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- break;
-
- case BC_dup2:
- INSTRUCTIONS_CHECK(2);
- OP(opcode);
- OP(ICMD_NOP);
- break;
-
- case BC_dup2_x1:
- INSTRUCTIONS_CHECK(7);
- OP(opcode);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- break;
-
- case BC_dup2_x2:
- INSTRUCTIONS_CHECK(9);
- OP(opcode);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- break;
-
- case BC_swap:
- INSTRUCTIONS_CHECK(3);
- OP(opcode);
- OP(ICMD_NOP);
- OP(ICMD_NOP);
- break;
-
- /* local variable access instructions *********************************/
-
- case BC_iload:
- case BC_fload:
- case BC_aload:
- if (iswide == false) {
- i = SUCK_BE_U1(m->jcode + bcindex + 1);
- }
- else {
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- nextbc = bcindex + 3;
- iswide = false;
- }
- OP_LOAD_ONEWORD(opcode, i, opcode - BC_iload);
- break;
-
- case BC_lload:
- case BC_dload:
- if (iswide == false) {
- i = SUCK_BE_U1(m->jcode + bcindex + 1);
- }
- else {
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- nextbc = bcindex + 3;
- iswide = false;
- }
- OP_LOAD_TWOWORD(opcode, i, opcode - BC_iload);
- break;
-
- case BC_iload_0:
- case BC_iload_1:
- case BC_iload_2:
- case BC_iload_3:
- OP_LOAD_ONEWORD(ICMD_ILOAD, opcode - BC_iload_0, TYPE_INT);
- break;
-
- case BC_lload_0:
- case BC_lload_1:
- case BC_lload_2:
- case BC_lload_3:
- OP_LOAD_TWOWORD(ICMD_LLOAD, opcode - BC_lload_0, TYPE_LNG);
- break;
-
- case BC_fload_0:
- case BC_fload_1:
- case BC_fload_2:
- case BC_fload_3:
- OP_LOAD_ONEWORD(ICMD_FLOAD, opcode - BC_fload_0, TYPE_FLT);
- break;
-
- case BC_dload_0:
- case BC_dload_1:
- case BC_dload_2:
- case BC_dload_3:
- OP_LOAD_TWOWORD(ICMD_DLOAD, opcode - BC_dload_0, TYPE_DBL);
- break;
-
- case BC_aload_0:
- case BC_aload_1:
- case BC_aload_2:
- case BC_aload_3:
- OP_LOAD_ONEWORD(ICMD_ALOAD, opcode - BC_aload_0, TYPE_ADR);
- break;
-
- case BC_istore:
- case BC_fstore:
- case BC_astore:
- if (iswide == false) {
- i = SUCK_BE_U1(m->jcode + bcindex + 1);
- }
- else {
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- nextbc = bcindex + 3;
- iswide = false;
- }
- OP_STORE_ONEWORD(opcode, i, opcode - BC_istore);
- break;
-
- case BC_lstore:
- case BC_dstore:
- if (iswide == false) {
- i = SUCK_BE_U1(m->jcode + bcindex + 1);
- }
- else {
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- nextbc = bcindex + 3;
- iswide = false;
- }
- OP_STORE_TWOWORD(opcode, i, opcode - BC_istore);
- break;
-
- case BC_istore_0:
- case BC_istore_1:
- case BC_istore_2:
- case BC_istore_3:
- OP_STORE_ONEWORD(ICMD_ISTORE, opcode - BC_istore_0, TYPE_INT);
- break;
-
- case BC_lstore_0:
- case BC_lstore_1:
- case BC_lstore_2:
- case BC_lstore_3:
- OP_STORE_TWOWORD(ICMD_LSTORE, opcode - BC_lstore_0, TYPE_LNG);
- break;
-
- case BC_fstore_0:
- case BC_fstore_1:
- case BC_fstore_2:
- case BC_fstore_3:
- OP_STORE_ONEWORD(ICMD_FSTORE, opcode - BC_fstore_0, TYPE_FLT);
- break;
-
- case BC_dstore_0:
- case BC_dstore_1:
- case BC_dstore_2:
- case BC_dstore_3:
- OP_STORE_TWOWORD(ICMD_DSTORE, opcode - BC_dstore_0, TYPE_DBL);
- break;
-
- case BC_astore_0:
- case BC_astore_1:
- case BC_astore_2:
- case BC_astore_3:
- OP_STORE_ONEWORD(ICMD_ASTORE, opcode - BC_astore_0, TYPE_ADR);
- break;
-
- case BC_iinc:
- {
- int v;
-
- if (iswide == false) {
- i = SUCK_BE_U1(m->jcode + bcindex + 1);
- v = SUCK_BE_S1(m->jcode + bcindex + 2);
- }
- else {
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- v = SUCK_BE_S2(m->jcode + bcindex + 3);
- nextbc = bcindex + 5;
- iswide = false;
- }
- INDEX_ONEWORD(i);
- LOCALTYPE_USED(i, TYPE_INT);
- OP_LOCALINDEX_I(opcode, i, v);
- }
- break;
-
- /* wider index for loading, storing and incrementing ******************/
-
- case BC_wide:
- bcindex++;
- iswide = true;
- goto fetch_opcode;
-
- /* managing arrays ****************************************************/
-
- case BC_newarray:
- switch (SUCK_BE_S1(m->jcode + bcindex + 1)) {
- case 4:
- bte = builtintable_get_internal(BUILTIN_newarray_boolean);
- break;
- case 5:
- bte = builtintable_get_internal(BUILTIN_newarray_char);
- break;
- case 6:
- bte = builtintable_get_internal(BUILTIN_newarray_float);
- break;
- case 7:
- bte = builtintable_get_internal(BUILTIN_newarray_double);
- break;
- case 8:
- bte = builtintable_get_internal(BUILTIN_newarray_byte);
- break;
- case 9:
- bte = builtintable_get_internal(BUILTIN_newarray_short);
- break;
- case 10:
- bte = builtintable_get_internal(BUILTIN_newarray_int);
- break;
- case 11:
- bte = builtintable_get_internal(BUILTIN_newarray_long);
- break;
-#if defined(ENABLE_VERIFIER)
- default:
- exceptions_throw_verifyerror(m, "Invalid array-type to create");
- return false;
-#endif
- }
- OP_BUILTIN_CHECK_EXCEPTION(bte);
- break;
-
- case BC_anewarray:
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- compr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class);
- if (compr == NULL)
- return false;
-
- if (!(cr = class_get_classref_multiarray_of(1, compr)))
- return false;
-
- if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
- return false;
-
- INSTRUCTIONS_CHECK(2);
- OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr);
- bte = builtintable_get_internal(BUILTIN_newarray);
- OP_BUILTIN_CHECK_EXCEPTION(bte);
- s_count++;
- break;
-
- case BC_multianewarray:
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- j = SUCK_BE_U1(m->jcode + bcindex + 3);
-
- cr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class);
- if (cr == NULL)
- return false;
-
- if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
- return false;
-
- /* if unresolved, c == NULL */
-
- iptr->s1.argcount = j;
- OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, INS_FLAG_CHECK);
- code_unflag_leafmethod(code);
- break;
-
- /* control flow instructions ******************************************/
-
- case BC_ifeq:
- case BC_iflt:
- case BC_ifle:
- case BC_ifne:
- case BC_ifgt:
- case BC_ifge:
- case BC_ifnull:
- case BC_ifnonnull:
- case BC_if_icmpeq:
- case BC_if_icmpne:
- case BC_if_icmplt:
- case BC_if_icmpgt:
- case BC_if_icmple:
- case BC_if_icmpge:
- case BC_if_acmpeq:
- case BC_if_acmpne:
- case BC_goto:
- i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1);
- CHECK_BYTECODE_INDEX(i);
- MARK_BASICBLOCK(&pd, i);
- blockend = true;
- OP_INSINDEX(opcode, i);
- break;
-
- case BC_goto_w:
- i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1);
- CHECK_BYTECODE_INDEX(i);
- MARK_BASICBLOCK(&pd, i);
- blockend = true;
- OP_INSINDEX(ICMD_GOTO, i);
- break;
-
- case BC_jsr:
- i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1);
-jsr_tail:
- CHECK_BYTECODE_INDEX(i);
- MARK_BASICBLOCK(&pd, i);
- blockend = true;
- OP_PREPARE_ZEROFLAGS(BC_jsr);
- iptr->sx.s23.s3.jsrtarget.insindex = i;
- PINC;
- break;
-
- case BC_jsr_w:
- i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1);
- goto jsr_tail;
-
- case BC_ret:
- if (iswide == false) {
- i = SUCK_BE_U1(m->jcode + bcindex + 1);
- }
- else {
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- nextbc = bcindex + 3;
- iswide = false;
- }
- blockend = true;
-
- OP_LOAD_ONEWORD(opcode, i, TYPE_ADR);
- break;
-
- case BC_ireturn:
- case BC_lreturn:
- case BC_freturn:
- case BC_dreturn:
- case BC_areturn:
- case BC_return:
- blockend = true;
- /* XXX ARETURN will need a flag in the typechecker */
- OP(opcode);
- break;
-
- case BC_athrow:
- blockend = true;
- /* XXX ATHROW will need a flag in the typechecker */
- OP(opcode);
- break;
-
-
- /* table jumps ********************************************************/
-
- case BC_lookupswitch:
- {
- s4 num, j;
- lookup_target_t *lookup;
-#if defined(ENABLE_VERIFIER)
- s4 prevvalue = 0;
-#endif
- blockend = true;
- nextbc = MEMORY_ALIGN((bcindex + 1), 4);
-
- CHECK_END_OF_BYTECODE(nextbc + 8);
-
- OP_PREPARE_ZEROFLAGS(opcode);
-
- /* default target */
-
- j = bcindex + SUCK_BE_S4(m->jcode + nextbc);
- iptr->sx.s23.s3.lookupdefault.insindex = j;
- nextbc += 4;
- CHECK_BYTECODE_INDEX(j);
- MARK_BASICBLOCK(&pd, j);
-
- /* number of pairs */
-
- num = SUCK_BE_U4(m->jcode + nextbc);
- iptr->sx.s23.s2.lookupcount = num;
- nextbc += 4;
-
- /* allocate the intermediate code table */
-
- lookup = DMNEW(lookup_target_t, num);
- iptr->dst.lookup = lookup;
-
- /* iterate over the lookup table */
-
- CHECK_END_OF_BYTECODE(nextbc + 8 * num);
-
- for (i = 0; i < num; i++) {
- /* value */
-
- j = SUCK_BE_S4(m->jcode + nextbc);
- lookup->value = j;
-
- nextbc += 4;
-
-#if defined(ENABLE_VERIFIER)
- /* check if the lookup table is sorted correctly */
-
- if (i && (j <= prevvalue)) {
- exceptions_throw_verifyerror(m, "Unsorted lookup switch");
- return false;
- }
- prevvalue = j;
-#endif
- /* target */
-
- j = bcindex + SUCK_BE_S4(m->jcode + nextbc);
- lookup->target.insindex = j;
- lookup++;
- nextbc += 4;
- CHECK_BYTECODE_INDEX(j);
- MARK_BASICBLOCK(&pd, j);
- }
-
- PINC;
- break;
- }
-
- case BC_tableswitch:
- {
- s4 num, j;
- s4 deftarget;
- branch_target_t *table;
-
- blockend = true;
- nextbc = MEMORY_ALIGN((bcindex + 1), 4);
-
- CHECK_END_OF_BYTECODE(nextbc + 12);
-
- OP_PREPARE_ZEROFLAGS(opcode);
-
- /* default target */
-
- deftarget = bcindex + SUCK_BE_S4(m->jcode + nextbc);
- nextbc += 4;
- CHECK_BYTECODE_INDEX(deftarget);
- MARK_BASICBLOCK(&pd, deftarget);
-
- /* lower bound */
-
- j = SUCK_BE_S4(m->jcode + nextbc);
- iptr->sx.s23.s2.tablelow = j;
- nextbc += 4;
-
- /* upper bound */
-
- num = SUCK_BE_S4(m->jcode + nextbc);
- iptr->sx.s23.s3.tablehigh = num;
- nextbc += 4;
-
- /* calculate the number of table entries */
-
- num = num - j + 1;
-
-#if defined(ENABLE_VERIFIER)
- if (num < 1) {
- exceptions_throw_verifyerror(m,
- "invalid TABLESWITCH: upper bound < lower bound");
- return false;
- }
-#endif
- /* create the intermediate code table */
- /* the first entry is the default target */
-
- table = DMNEW(branch_target_t, 1 + num);
- iptr->dst.table = table;
- (table++)->insindex = deftarget;
-
- /* iterate over the target table */
-
- CHECK_END_OF_BYTECODE(nextbc + 4 * num);
-
- for (i = 0; i < num; i++) {
- j = bcindex + SUCK_BE_S4(m->jcode + nextbc);
- (table++)->insindex = j;
- nextbc += 4;
- CHECK_BYTECODE_INDEX(j);
- MARK_BASICBLOCK(&pd, j);
- }
-
- PINC;
- break;
- }
-
-
- /* load and store of object fields ************************************/
-
- case BC_aastore:
- OP(opcode);
- code_unflag_leafmethod(code);
- break;
-
- case BC_getstatic:
- case BC_putstatic:
- case BC_getfield:
- case BC_putfield:
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- fmi = class_getconstant(m->clazz, i, CONSTANT_Fieldref);
-
- if (fmi == NULL)
- return false;
-
- OP_PREPARE_ZEROFLAGS(opcode);
- iptr->sx.s23.s3.fmiref = fmi;
-
- /* only with -noverify, otherwise the typechecker does this */
-
-#if defined(ENABLE_VERIFIER)
- if (!JITDATA_HAS_FLAG_VERIFY(jd)) {
-#endif
- result = resolve_field_lazy(m, fmi);
-
- if (result == resolveFailed)
- return false;
-
- if (result != resolveSucceeded) {
- uf = resolve_create_unresolved_field(m->clazz, m, iptr);
-
- if (uf == NULL)
- return false;
-
- /* store the unresolved_field pointer */
-
- iptr->sx.s23.s3.uf = uf;
- iptr->flags.bits |= INS_FLAG_UNRESOLVED;
- }
-#if defined(ENABLE_VERIFIER)
- }
-#endif
- PINC;
- break;
-
-
- /* method invocation **************************************************/
-
- case BC_invokestatic:
- OP_PREPARE_ZEROFLAGS(opcode);
-
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- fmi = class_getconstant(m->clazz, i, CONSTANT_Methodref);
-
- if (fmi == NULL)
- return false;
-
- md = fmi->parseddesc.md;
-
- if (md->params == NULL)
- if (!descriptor_params_from_paramtypes(md, ACC_STATIC))
- return false;
-
- goto invoke_method;
-
- case BC_invokespecial:
- OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK);
-
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- fmi = class_getconstant(m->clazz, i, CONSTANT_Methodref);
-
- goto invoke_nonstatic_method;
-
- case BC_invokeinterface:
- OP_PREPARE_ZEROFLAGS(opcode);
-
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- fmi = class_getconstant(m->clazz, i, CONSTANT_InterfaceMethodref);
-
- goto invoke_nonstatic_method;
-
- case BC_invokevirtual:
- OP_PREPARE_ZEROFLAGS(opcode);
-
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- fmi = class_getconstant(m->clazz, i, CONSTANT_Methodref);
-
-invoke_nonstatic_method:
- if (fmi == NULL)
- return false;
-
- md = fmi->parseddesc.md;
-
- if (md->params == NULL)
- if (!descriptor_params_from_paramtypes(md, 0))
- return false;
-
-invoke_method:
- code_unflag_leafmethod(code);
-
- iptr->sx.s23.s3.fmiref = fmi;
-
- /* only with -noverify, otherwise the typechecker does this */
-
-#if defined(ENABLE_VERIFIER)
- if (!JITDATA_HAS_FLAG_VERIFY(jd)) {
-#endif
- result = resolve_method_lazy(m, fmi,
- (opcode == BC_invokespecial));
-
- if (result == resolveFailed)
- return false;
-
- if (result == resolveSucceeded) {
- methodinfo *mi = iptr->sx.s23.s3.fmiref->p.method;
-
- /* if this call is monomorphic, turn it into an
- INVOKESPECIAL */
-
- assert(IS_FMIREF_RESOLVED(iptr->sx.s23.s3.fmiref));
-
- if ((iptr->opc == ICMD_INVOKEVIRTUAL)
- && (mi->flags & (ACC_FINAL | ACC_PRIVATE)))
- {
- iptr->opc = ICMD_INVOKESPECIAL;
- iptr->flags.bits |= INS_FLAG_CHECK;
- }
- }
- else {
- um = resolve_create_unresolved_method(m->clazz, m, fmi,
- (opcode == BC_invokestatic),
- (opcode == BC_invokespecial));
-
- if (um == NULL)
- return false;
-
- /* store the unresolved_method pointer */
-
- iptr->sx.s23.s3.um = um;
- iptr->flags.bits |= INS_FLAG_UNRESOLVED;
- }
-#if defined(ENABLE_VERIFIER)
- }
-#endif
- PINC;
- break;
-
- /* instructions taking class arguments ********************************/
-
- case BC_new:
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- cr = class_getconstant(m->clazz, i, CONSTANT_Class);
-
- if (cr == NULL)
- return false;
-
- if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
- return false;
-
- INSTRUCTIONS_CHECK(2);
- OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr);
- bte = builtintable_get_internal(BUILTIN_new);
- OP_BUILTIN_CHECK_EXCEPTION(bte);
- s_count++;
- break;
-
- case BC_checkcast:
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- cr = class_getconstant(m->clazz, i, CONSTANT_Class);
-
- if (cr == NULL)
- return false;
-
- if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
- return false;
-
- if (cr->name->text[0] == '[') {
- /* array type cast-check */
- flags = INS_FLAG_CHECK | INS_FLAG_ARRAY;
- code_unflag_leafmethod(code);
- }
- else {
- /* object type cast-check */
- flags = INS_FLAG_CHECK;
- }
- OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, flags);
- break;
-
- case BC_instanceof:
- i = SUCK_BE_U2(m->jcode + bcindex + 1);
- cr = class_getconstant(m->clazz, i, CONSTANT_Class);
-
- if (cr == NULL)
- return false;
-
- if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
- return false;
-
- if (cr->name->text[0] == '[') {
- /* array type cast-check */
- INSTRUCTIONS_CHECK(2);
- OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr);
- bte = builtintable_get_internal(BUILTIN_arrayinstanceof);
- OP_BUILTIN_NO_EXCEPTION(bte);
- s_count++;
- }
- else {
- /* object type cast-check */
- OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, 0 /* flags*/);
- }
- break;
-
- /* synchronization instructions ***************************************/
-
- case BC_monitorenter:
-#if defined(ENABLE_THREADS)
- if (checksync) {
- bte = builtintable_get_internal(LOCK_monitor_enter);
- OP_BUILTIN_CHECK_EXCEPTION(bte);
- }
- else
-#endif
- {
- OP_CHECK_EXCEPTION(ICMD_CHECKNULL);
- OP(ICMD_POP);
- }
- break;
-
- case BC_monitorexit:
-#if defined(ENABLE_THREADS)
- if (checksync) {
- bte = builtintable_get_internal(LOCK_monitor_exit);
- OP_BUILTIN_CHECK_EXCEPTION(bte);
- }
- else
-#endif
- {
- OP_CHECK_EXCEPTION(ICMD_CHECKNULL);
- OP(ICMD_POP);
- }
- break;
-
- /* arithmetic instructions that may become builtin functions **********/
-
- case BC_idiv:
-#if !SUPPORT_DIVISION
- bte = builtintable_get_internal(BUILTIN_idiv);
- OP_BUILTIN_ARITHMETIC(opcode, bte);
-#else
-# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
- OP(opcode);
-# else
- OP_CHECK_EXCEPTION(opcode);
-# endif
-#endif
- break;
-
- case BC_irem:
-#if !SUPPORT_DIVISION
- bte = builtintable_get_internal(BUILTIN_irem);
- OP_BUILTIN_ARITHMETIC(opcode, bte);
-#else
-# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
- OP(opcode);
-# else
- OP_CHECK_EXCEPTION(opcode);
-# endif
-#endif
- break;
-
- case BC_ldiv:
-#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV)
- bte = builtintable_get_internal(BUILTIN_ldiv);
- OP_BUILTIN_ARITHMETIC(opcode, bte);
-#else
-# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
- OP(opcode);
-# else
- OP_CHECK_EXCEPTION(opcode);
-# endif
-#endif
- break;
-
- case BC_lrem:
-#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV)
- bte = builtintable_get_internal(BUILTIN_lrem);
- OP_BUILTIN_ARITHMETIC(opcode, bte);
-#else
-# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
- OP(opcode);
-# else
- OP_CHECK_EXCEPTION(opcode);
-# endif
-#endif
- break;
-
- case BC_frem:
-#if defined(__I386__)
- OP(opcode);
-#else
- bte = builtintable_get_internal(BUILTIN_frem);
- OP_BUILTIN_NO_EXCEPTION(bte);
-#endif
- break;
-
- case BC_drem:
-#if defined(__I386__)
- OP(opcode);
-#else
- bte = builtintable_get_internal(BUILTIN_drem);
- OP_BUILTIN_NO_EXCEPTION(bte);
-#endif
- break;
-
- case BC_f2i:
-#if defined(__ALPHA__)
- bte = builtintable_get_internal(BUILTIN_f2i);
- OP_BUILTIN_NO_EXCEPTION(bte);
-#else
- OP(opcode);
-#endif
- break;
-
- case BC_f2l:
-#if defined(__ALPHA__)
- bte = builtintable_get_internal(BUILTIN_f2l);
- OP_BUILTIN_NO_EXCEPTION(bte);
-#else
- OP(opcode);
-#endif
- break;
-
- case BC_d2i:
-#if defined(__ALPHA__)
- bte = builtintable_get_internal(BUILTIN_d2i);
- OP_BUILTIN_NO_EXCEPTION(bte);
-#else
- OP(opcode);
-#endif
- break;
-
- case BC_d2l:
-#if defined(__ALPHA__)
- bte = builtintable_get_internal(BUILTIN_d2l);
- OP_BUILTIN_NO_EXCEPTION(bte);
-#else
- OP(opcode);
-#endif
- break;
-
-
- /* invalid opcodes ****************************************************/
-
- /* check for invalid opcodes if the verifier is enabled */
-#if defined(ENABLE_VERIFIER)
- case BC_breakpoint:
- exceptions_throw_verifyerror(m, "Quick instructions shouldn't appear, yet.");
- return false;
-
-
- /* Unused opcodes ************************************************** */
-
- case 186:
- case 203:
- case 204:
- case 205:
- case 206:
- case 207:
- case 208:
- case 209:
- case 210:
- case 211:
- case 212:
- case 213:
- case 214:
- case 215:
- case 216:
- case 217:
- case 218:
- case 219:
- case 220:
- case 221:
- case 222:
- case 223:
- case 224:
- case 225:
- case 226:
- case 227:
- case 228:
- case 229:
- case 230:
- case 231:
- case 232:
- case 233:
- case 234:
- case 235:
- case 236:
- case 237:
- case 238:
- case 239:
- case 240:
- case 241:
- case 242:
- case 243:
- case 244:
- case 245:
- case 246:
- case 247:
- case 248:
- case 249:
- case 250:
- case 251:
- case 252:
- case 253:
- case 254:
- case 255:
- exceptions_throw_verifyerror(m, "Illegal opcode %d at instr %d\n",
- opcode, ircount);
- return false;
- break;
-#endif /* defined(ENABLE_VERIFIER) */
-
- /* opcodes that don't require translation *****************************/
-
- default:
- /* Straight-forward translation to HIR. */
- OP(opcode);
- break;
-
- } /* end switch */
-
- /* verifier checks ****************************************************/
-
-#if defined(ENABLE_VERIFIER)
- /* If WIDE was used correctly, iswide should have been reset by now. */
- if (iswide) {
- exceptions_throw_verifyerror(m,
- "Illegal instruction: WIDE before incompatible opcode");
- return false;
- }
-#endif /* defined(ENABLE_VERIFIER) */
-
- } /* end for */
-
- if (JITDATA_HAS_FLAG_REORDER(jd)) {
- /* add a NOP to the last basic block */
-
- INSTRUCTIONS_CHECK(1);
- OP(ICMD_NOP);
- }
-
- /*** END OF LOOP **********************************************************/
-
- /* assert that we did not write more ICMDs than allocated */
-
- assert(ircount <= pd.instructionslength);
- assert(ircount == (iptr - pd.instructions));
-
- /*** verifier checks ******************************************************/
-
-#if defined(ENABLE_VERIFIER)
- if (bcindex != m->jcodelength) {
- exceptions_throw_verifyerror(m,
- "Command-sequence crosses code-boundary");
- return false;
- }
-
- if (!blockend) {
- exceptions_throw_verifyerror(m, "Falling off the end of the code");
- return false;
- }
-#endif /* defined(ENABLE_VERIFIER) */
-
- /*** setup the methodinfo, allocate stack and basic blocks ****************/
-
- /* identify basic blocks */
-
- /* check if first instruction is a branch target */
-
- if (pd.basicblockstart[0] == 1) {
- jd->branchtoentry = true;
- }
- else {
- /* first instruction always starts a basic block */
-
- iptr = pd.instructions;
-
- iptr->flags.bits |= INS_FLAG_BASICBLOCK;
- }
-
- /* Iterate over all bytecode instructions and set missing
- basic-block starts in IR instructions. */
-
- for (bcindex = 0; bcindex < m->jcodelength; bcindex++) {
- /* Does the current bytecode instruction start a basic
- block? */
-
- if (pd.basicblockstart[bcindex] == 1) {
-#if defined(ENABLE_VERIFIER)
- /* Check if this bytecode basic-block start at the
- beginning of a bytecode instruction. */
-
- if (pd.bytecodestart[bcindex] == 0) {
- exceptions_throw_verifyerror(m,
- "Branch into middle of instruction");
- return false;
- }
-#endif
-
- /* Get the IR instruction mapped to the bytecode
- instruction and set the basic block flag. */
-
- irindex = pd.bytecodemap[bcindex];
- iptr = pd.instructions + irindex;
-
- iptr->flags.bits |= INS_FLAG_BASICBLOCK;
- }
- }
-
- /* IR instruction index to basic-block index mapping */
-
- pd.instructionmap = DMNEW(s4, ircount);
- MZERO(pd.instructionmap, s4, ircount);
-
- /* Iterate over all IR instructions and count the basic blocks. */
-
- iptr = pd.instructions;
-
- bbcount = 0;
-
- for (i = 0; i < ircount; i++, iptr++) {
- if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) {
- /* store the basic-block number in the IR instruction
- map */
-
- pd.instructionmap[i] = bbcount;
-
- /* post-increment the basic-block count */
-
- bbcount++;
- }
- }
-
- /* Allocate basic block array (one more for end ipc). */
-
- jd->basicblocks = DMNEW(basicblock, bbcount + 1);
- MZERO(jd->basicblocks, basicblock, bbcount + 1);
-
- /* Now iterate again over all IR instructions and initialize the
- basic block structures and, in the same loop, resolve the
- branch-target instruction indices to basic blocks. */
-
- iptr = pd.instructions;
- bptr = jd->basicblocks;
-
- bbcount = 0;
-
- for (i = 0; i < ircount; i++, iptr++) {
- /* check for basic block */
-
- if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) {
- /* intialize the basic block */
-
- BASICBLOCK_INIT(bptr, m);
-
- bptr->iinstr = iptr;
-
- if (bbcount > 0) {
- bptr[-1].icount = bptr->iinstr - bptr[-1].iinstr;
- }
-
- /* bptr->icount is set when the next block is allocated */
-
- bptr->nr = bbcount++;
- bptr++;
- bptr[-1].next = bptr;
- }
-
- /* resolve instruction indices to basic blocks */
-
- switch (iptr->opc) {
- case ICMD_IFEQ:
- case ICMD_IFLT:
- case ICMD_IFLE:
- case ICMD_IFNE:
- case ICMD_IFGT:
- case ICMD_IFGE:
- case ICMD_IFNULL:
- case ICMD_IFNONNULL:
- case ICMD_IF_ICMPEQ:
- case ICMD_IF_ICMPNE:
- case ICMD_IF_ICMPLT:
- case ICMD_IF_ICMPGT:
- case ICMD_IF_ICMPLE:
- case ICMD_IF_ICMPGE:
- case ICMD_IF_ACMPEQ:
- case ICMD_IF_ACMPNE:
- case ICMD_GOTO:
- BYTECODEINDEX_TO_BASICBLOCK(iptr->dst);
- break;
-
- case ICMD_JSR:
- BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.jsrtarget);
- break;
-
- case ICMD_TABLESWITCH:
- table = iptr->dst.table;
-
- BYTECODEINDEX_TO_BASICBLOCK(*table);
- table++;
-
- j = iptr->sx.s23.s3.tablehigh - iptr->sx.s23.s2.tablelow + 1;
-
- while (--j >= 0) {
- BYTECODEINDEX_TO_BASICBLOCK(*table);
- table++;
- }
- break;
-
- case ICMD_LOOKUPSWITCH:
- BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.lookupdefault);
-
- lookup = iptr->dst.lookup;
-
- j = iptr->sx.s23.s2.lookupcount;
-
- while (--j >= 0) {
- BYTECODEINDEX_TO_BASICBLOCK(lookup->target);
- lookup++;
- }
- break;
- }
- }
-
- /* set instruction count of last real block */
-
- if (bbcount > 0) {
- bptr[-1].icount = (pd.instructions + ircount) - bptr[-1].iinstr;
- }
-
- /* allocate additional block at end */
-
- BASICBLOCK_INIT(bptr, m);
- bptr->nr = bbcount;
-
- /* set basicblock pointers in exception table */
-
- if (!parse_resolve_exception_table(jd, &pd))
- return false;
-
- /* store the local map */
-
- jd->local_map = local_map;
-
- /* calculate local variable renaming */
-
- {
- s4 nlocals = 0;
- s4 i;
- s4 t;
- s4 varindex;
- s4 *mapptr;
- s4 *reversemap;
-
- mapptr = local_map;
-
- /* iterate over local_map[0..m->maxlocals*5-1] and allocate a unique */
- /* variable index for each _used_ (javaindex,type) pair. */
- /* (local_map[javaindex*5+type] = cacaoindex) */
- /* Unused (javaindex,type) pairs are marked with UNUSED. */
-
- for (i = 0; i < (m->maxlocals * 5); i++, mapptr++) {
- if (*mapptr)
- *mapptr = nlocals++;
- else
- *mapptr = UNUSED;
- }
-
- jd->localcount = nlocals;
-
- /* calculate the (maximum) number of variables needed */
-
- jd->varcount =
- nlocals /* local variables */
- + bbcount * m->maxstack /* invars */
- + s_count; /* variables created within blocks (non-invar) */
-
- /* reserve the first indices for local variables */
-
- jd->vartop = nlocals;
-
- /* reserve extra variables needed by stack analyse */
-
- jd->varcount += STACK_EXTRA_VARS;
- jd->vartop += STACK_EXTRA_VARS;
-
- /* The verifier needs space for saving invars in some cases and */
- /* extra variables. */
-
-#if defined(ENABLE_VERIFIER)
- jd->varcount += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack;
- jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack;
-#endif
- /* allocate and initialize the variable array */
-
- jd->var = DMNEW(varinfo, jd->varcount);
- MZERO(jd->var, varinfo, jd->varcount);
-
- /* set types of all locals in jd->var */
- /* and fill the reverselocalmap */
-
- reversemap = DMNEW(s4, nlocals);
-
- for (i = 0; i < m->maxlocals; i++)
- for (t=0; t<5; t++) {
- varindex = local_map[5*i + t];
- if (varindex != UNUSED) {
- VAR(varindex)->type = t;
- reversemap[varindex] = i;
- }
- }
-
- jd->reverselocalmap = reversemap;
- }
-
- /* assign local variables to method variables */
-
- jd->instructions = pd.instructions;
- jd->instructioncount = ircount;
- jd->basicblockcount = bbcount;
- jd->stackcount = s_count + bbcount * m->maxstack; /* in-stacks */
-
- /* allocate stack table */
-
- jd->stack = DMNEW(stackelement_t, jd->stackcount);
-
- /* everything's ok */
-
- return true;
-
- /*** goto labels for throwing verifier exceptions *************************/
-
-#if defined(ENABLE_VERIFIER)
-
-throw_unexpected_end_of_bytecode:
- exceptions_throw_verifyerror(m, "Unexpected end of bytecode");
- return false;
-
-throw_invalid_bytecode_index:
- exceptions_throw_verifyerror(m, "Illegal target of branch instruction");
- return false;
-
-throw_illegal_local_variable_number:
- exceptions_throw_verifyerror(m, "Illegal local variable number");
- return false;
-
-#endif /* ENABLE_VERIFIER */
-}
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/parse.c - parser for JavaVM to intermediate code translation
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "native/native.hpp"
+
+#include "threads/lock.hpp"
+
+#include "toolbox/logging.h"
+
+#include "vm/jit/builtin.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/global.h"
+#include "vm/linker.hpp"
+#include "vm/loader.hpp"
+#include "vm/options.h"
+#include "vm/resolve.hpp"
+
+#if defined(ENABLE_STATISTICS)
+# include "vm/statistics.h"
+#endif
+
+#include "vm/string.hpp"
+#include "vm/suck.hpp"
+
+#include "vm/jit/asmpart.h"
+#include "vm/jit/jit.hpp"
+#include "vm/jit/parse.hpp"
+#include "vm/jit/loop/loop.h"
+
+#include "vm/jit/ir/bytecode.h"
+
+
+#define INSTRUCTIONS_INCREMENT 5 /* number of additional instructions to */
+ /* allocate if space runs out */
+
+
+/* local macros ***************************************************************/
+
+#define BYTECODEINDEX_TO_BASICBLOCK(dst) \
+ do { \
+ (dst).block = \
+ parse_bytecodeindex_to_basicblock(jd, &pd, (dst).insindex); \
+ } while (0)
+
+
+/* parserdata_t ***************************************************************/
+
+typedef struct parsedata_t parsedata_t;
+
+struct parsedata_t {
+ u1 *bytecodestart; /* start of bytecode instructions */
+ u1 *basicblockstart; /* start of bytecode basic-blocks */
+
+ s4 *bytecodemap; /* bytecode to IR mapping */
+
+ instruction *instructions; /* instruction array */
+ s4 instructionslength; /* length of the instruction array */
+
+ s4 *instructionmap; /* IR to basic-block mapping */
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* parse_setup *****************************************************************
+
+ Fills the passed parsedata_t structure.
+
+*******************************************************************************/
+
+static void parse_setup(jitdata *jd, parsedata_t *pd)
+{
+ methodinfo *m;
+
+ /* get required compiler data */
+
+ m = jd->m;
+
+ /* bytecode start array */
+
+ pd->bytecodestart = (u1*) DumpMemory::allocate(sizeof(u1) * (m->jcodelength + 1));
+ MZERO(pd->bytecodestart, u1, m->jcodelength + 1);
+
+ /* bytecode basic-block start array */
+
+ pd->basicblockstart = (u1*) DumpMemory::allocate(sizeof(u1) *(m->jcodelength + 1));
+ MZERO(pd->basicblockstart, u1, m->jcodelength + 1);
+
+ /* bytecode instruction index to IR instruction mapping */
+
+ pd->bytecodemap = (s4*) DumpMemory::allocate(sizeof(s4) * (m->jcodelength + 1));
+ MSET(pd->bytecodemap, -1, s4, m->jcodelength + 1);
+
+ /* allocate the instruction array */
+
+ pd->instructionslength = m->jcodelength + 1;
+ pd->instructions = (instruction*) DumpMemory::allocate(sizeof(instruction) * pd->instructionslength);
+
+ /* Zero the intermediate instructions array so we don't have any
+ invalid pointers in it if we cannot finish stack_analyse(). */
+
+ MZERO(pd->instructions, instruction, pd->instructionslength);
+
+ /* The instructionmap is allocated later when we know the count of
+ instructions. */
+
+ pd->instructionmap = NULL;
+}
+
+
+/* parse_realloc_instructions **************************************************
+
+ Reallocate the instructions array so there is room for at least N
+ additional instructions.
+
+ RETURN VALUE:
+ the new value for iptr
+
+*******************************************************************************/
+
+static instruction *parse_realloc_instructions(parsedata_t *pd, s4 icount, s4 n)
+{
+ /* increase the size of the instruction array */
+
+ pd->instructionslength += (n + INSTRUCTIONS_INCREMENT);
+
+ /* reallocate the array */
+
+ pd->instructions = (instruction*) DumpMemory::reallocate(pd->instructions, sizeof(instruction) * icount,
+ sizeof(instruction) * pd->instructionslength);
+ MZERO(pd->instructions + icount, instruction,
+ (pd->instructionslength - icount));
+
+ /* return the iptr */
+
+ return pd->instructions + icount;
+}
+
+
+/* parse_bytecodeindex_to_basicblock *******************************************
+
+ Resolves a bytecode index to the corresponding basic block.
+
+*******************************************************************************/
+
+static basicblock *parse_bytecodeindex_to_basicblock(jitdata *jd,
+ parsedata_t *pd,
+ s4 bcindex)
+{
+ s4 irindex;
+ basicblock *bb;
+
+ irindex = pd->bytecodemap[bcindex];
+ bb = jd->basicblocks + pd->instructionmap[irindex];
+
+ return bb;
+}
+
+
+/* parse_mark_exception_boundaries *********************************************
+
+ Mark exception handlers and the boundaries of the handled regions as
+ basic block boundaries.
+
+ IN:
+ jd...............current jitdata
+
+ RETURN VALUE:
+ true.............everything ok
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+static bool parse_mark_exception_boundaries(jitdata *jd, parsedata_t *pd)
+{
+ s4 bcindex;
+ s4 i;
+ s4 len;
+ raw_exception_entry *rex;
+ methodinfo *m;
+
+ m = jd->m;
+
+ len = m->rawexceptiontablelength;
+
+ if (len == 0)
+ return true;
+
+ rex = m->rawexceptiontable;
+
+ for (i = 0; i < len; ++i, ++rex) {
+
+ /* the start of the handled region becomes a basic block start */
+
+ bcindex = rex->startpc;
+ CHECK_BYTECODE_INDEX(bcindex);
+ MARK_BASICBLOCK(pd, bcindex);
+
+ bcindex = rex->endpc; /* see JVM Spec 4.7.3 */
+ CHECK_BYTECODE_INDEX_EXCLUSIVE(bcindex);
+
+ /* check that the range is valid */
+
+#if defined(ENABLE_VERIFIER)
+ if (bcindex <= rex->startpc) {
+ exceptions_throw_verifyerror(m, "Invalid exception handler range");
+ return false;
+ }
+#endif
+
+ /* End of handled region becomes a basic block boundary (if it
+ is the bytecode end, we'll use the special end block that
+ is created anyway). */
+
+ if (bcindex < m->jcodelength)
+ MARK_BASICBLOCK(pd, bcindex);
+ else
+ jd->branchtoend = true;
+
+ /* the start of the handler becomes a basic block start */
+
+ bcindex = rex->handlerpc;
+ CHECK_BYTECODE_INDEX(bcindex);
+ MARK_BASICBLOCK(pd, bcindex);
+ }
+
+ /* everything ok */
+
+ return true;
+
+#if defined(ENABLE_VERIFIER)
+throw_invalid_bytecode_index:
+ exceptions_throw_verifyerror(m,
+ "Illegal bytecode index in exception table");
+ return false;
+#endif
+}
+
+
+/* parse_resolve_exception_table ***********************************************
+
+ Enter the exception handlers and their ranges, resolved to basicblock *s,
+ in the jitdata.
+
+ IN:
+ jd...............current jitdata
+
+ RETURN VALUE:
+ true.............everything ok
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+static bool parse_resolve_exception_table(jitdata *jd, parsedata_t *pd)
+{
+ methodinfo *m;
+ raw_exception_entry *rex;
+ exception_entry *ex;
+ s4 i;
+ s4 len;
+ classinfo *exclass;
+
+ m = jd->m;
+
+ len = m->rawexceptiontablelength;
+
+ /* common case: no handler entries */
+
+ if (len == 0)
+ return true;
+
+ /* allocate the exception table */
+
+ jd->exceptiontablelength = len;
+ jd->exceptiontable = (exception_entry*) DumpMemory::allocate(sizeof(exception_entry) * (len + 1)); /* XXX why +1? */
+
+ /* copy and resolve the entries */
+
+ ex = jd->exceptiontable;
+ rex = m->rawexceptiontable;
+
+ for (i = 0; i < len; ++i, ++rex, ++ex) {
+ /* resolve instruction indices to basic blocks */
+
+ ex->start = parse_bytecodeindex_to_basicblock(jd, pd, rex->startpc);
+ ex->end = parse_bytecodeindex_to_basicblock(jd, pd, rex->endpc);
+ ex->handler = parse_bytecodeindex_to_basicblock(jd, pd, rex->handlerpc);
+
+ /* lazily resolve the catchtype */
+
+ if (rex->catchtype.any != NULL) {
+ if (!resolve_classref_or_classinfo(m,
+ rex->catchtype,
+ resolveLazy, true, false,
+ &exclass))
+ return false;
+
+ /* if resolved, enter the result of resolution in the table */
+
+ if (exclass != NULL)
+ rex->catchtype.cls = exclass;
+ }
+
+ ex->catchtype = rex->catchtype;
+ ex->next = NULL; /* set by loop analysis */
+ ex->down = ex + 1; /* link to next exception entry */
+ }
+
+ /* terminate the ->down linked list */
+
+ assert(ex != jd->exceptiontable);
+ ex[-1].down = NULL;
+
+ return true;
+}
+
+
+/*******************************************************************************
+
+ function 'parse' scans the JavaVM code and generates intermediate code
+
+ During parsing the block index table is used to store at bit pos 0
+ a flag which marks basic block starts and at position 1 to 31 the
+ intermediate instruction index. After parsing the block index table
+ is scanned, for marked positions a block is generated and the block
+ number is stored in the block index table.
+
+*******************************************************************************/
+
+/*** macro for checking the length of the bytecode ***/
+
+#if defined(ENABLE_VERIFIER)
+#define CHECK_END_OF_BYTECODE(neededlength) \
+ do { \
+ if ((neededlength) > m->jcodelength) \
+ goto throw_unexpected_end_of_bytecode; \
+ } while (0)
+#else /* !ENABLE_VERIFIER */
+#define CHECK_END_OF_BYTECODE(neededlength)
+#endif /* ENABLE_VERIFIER */
+
+bool parse(jitdata *jd)
+{
+ methodinfo *m; /* method being parsed */
+ codeinfo *code;
+ parsedata_t pd;
+ instruction *iptr; /* current ptr into instruction array */
+
+ s4 bcindex; /* bytecode instruction index */
+ s4 nextbc; /* start of next bytecode instruction */
+ s4 opcode; /* bytecode instruction opcode */
+
+ s4 irindex; /* IR instruction index */
+ s4 ircount; /* IR instruction count */
+
+ s4 bbcount; /* basic block count */
+
+ int s_count = 0; /* stack element counter */
+ bool blockend; /* true if basic block end has been reached */
+ bool iswide; /* true if last instruction was a wide */
+
+ constant_classref *cr;
+ constant_classref *compr;
+ classinfo *c;
+ builtintable_entry *bte;
+ constant_FMIref *fmi;
+ methoddesc *md;
+ unresolved_method *um;
+ unresolved_field *uf;
+
+ resolve_result_t result;
+ u2 lineindex = 0;
+ u2 currentline = 0;
+ u2 linepcchange = 0;
+ u4 flags;
+ basicblock *bptr;
+
+ int *local_map; /* local pointer to renaming map */
+ /* is assigned to rd->local_map at the end */
+ branch_target_t *table;
+ lookup_target_t *lookup;
+ s4 i;
+ s4 j;
+
+ /* get required compiler data */
+
+ m = jd->m;
+ code = jd->code;
+
+ /* allocate buffers for local variable renaming */
+
+ local_map = (int*) DumpMemory::allocate(sizeof(int) * m->maxlocals * 5);
+
+ for (i = 0; i < m->maxlocals; i++) {
+ local_map[i * 5 + 0] = 0;
+ local_map[i * 5 + 1] = 0;
+ local_map[i * 5 + 2] = 0;
+ local_map[i * 5 + 3] = 0;
+ local_map[i * 5 + 4] = 0;
+ }
+
+ /* initialize the parse data structures */
+
+ parse_setup(jd, &pd);
+
+ /* initialize local variables */
+
+ iptr = pd.instructions;
+ ircount = 0;
+ bbcount = 0;
+ blockend = false;
+ iswide = false;
+
+ /* mark basic block boundaries for exception table */
+
+ if (!parse_mark_exception_boundaries(jd, &pd))
+ return false;
+
+ /* initialize stack element counter */
+
+ s_count = 1 + m->rawexceptiontablelength;
+
+ /* setup line number info */
+
+ currentline = 0;
+ linepcchange = 0;
+
+ if (m->linenumbercount == 0) {
+ lineindex = 0;
+ }
+ else {
+ linepcchange = m->linenumbers[0].start_pc;
+ }
+
+ /*** LOOP OVER ALL BYTECODE INSTRUCTIONS **********************************/
+
+ for (bcindex = 0; bcindex < m->jcodelength; bcindex = nextbc) {
+
+ /* mark this position as a valid bytecode instruction start */
+
+ pd.bytecodestart[bcindex] = 1;
+
+ /* change the current line number, if necessary */
+
+ /* XXX rewrite this using pointer arithmetic */
+
+ if (linepcchange == bcindex) {
+ if (m->linenumbercount > lineindex) {
+next_linenumber:
+ currentline = m->linenumbers[lineindex].line_number;
+ lineindex++;
+ if (lineindex < m->linenumbercount) {
+ linepcchange = m->linenumbers[lineindex].start_pc;
+ if (linepcchange == bcindex)
+ goto next_linenumber;
+ }
+ }
+ }
+
+fetch_opcode:
+ /* fetch next opcode */
+
+ opcode = SUCK_BE_U1(m->jcode + bcindex);
+
+ /* If the previous instruction was a block-end instruction,
+ mark the current bytecode instruction as basic-block
+ starting instruction. */
+
+ /* NOTE: Some compilers put a BC_nop after a blockend
+ instruction. */
+
+ if (blockend && (opcode != BC_nop)) {
+ MARK_BASICBLOCK(&pd, bcindex);
+ blockend = false;
+ }
+
+ /* If the current bytecode instruction was marked as
+ basic-block starting instruction before (e.g. blockend,
+ forward-branch target), mark the current IR instruction
+ too. */
+
+ if (pd.basicblockstart[bcindex] != 0) {
+ /* We need a NOP as last instruction in each basic block
+ for basic block reordering (may be replaced with a GOTO
+ later). */
+
+ INSTRUCTIONS_CHECK(1);
+ OP(ICMD_NOP);
+ }
+
+ /* store intermediate instruction count (bit 0 mark block starts) */
+
+ pd.bytecodemap[bcindex] = ircount;
+
+ /* compute next instruction start */
+
+ nextbc = bcindex + bytecode[opcode].length;
+
+ CHECK_END_OF_BYTECODE(nextbc);
+
+ /* add stack elements produced by this instruction */
+
+ s_count += bytecode[opcode].slots;
+
+ /* We check here for the space of 1 instruction in the
+ instruction array. If an opcode is converted to more than
+ 1 instruction, this is checked in the corresponding
+ case. */
+
+ INSTRUCTIONS_CHECK(1);
+
+ /* translate this bytecode instruction */
+ switch (opcode) {
+
+ case BC_nop:
+ break;
+
+ /* pushing constants onto the stack ***********************************/
+
+ case BC_bipush:
+ OP_LOADCONST_I(SUCK_BE_S1(m->jcode + bcindex + 1));
+ break;
+
+ case BC_sipush:
+ OP_LOADCONST_I(SUCK_BE_S2(m->jcode + bcindex + 1));
+ break;
+
+ case BC_ldc1:
+ i = SUCK_BE_U1(m->jcode + bcindex + 1);
+ goto pushconstantitem;
+
+ case BC_ldc2:
+ case BC_ldc2w:
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+
+ pushconstantitem:
+
+#if defined(ENABLE_VERIFIER)
+ if (i >= m->clazz->cpcount) {
+ exceptions_throw_verifyerror(m,
+ "Attempt to access constant outside range");
+ return false;
+ }
+#endif
+
+ switch (m->clazz->cptags[i]) {
+ case CONSTANT_Integer:
+ OP_LOADCONST_I(((constant_integer *) (m->clazz->cpinfos[i]))->value);
+ break;
+ case CONSTANT_Long:
+ OP_LOADCONST_L(((constant_long *) (m->clazz->cpinfos[i]))->value);
+ break;
+ case CONSTANT_Float:
+ OP_LOADCONST_F(((constant_float *) (m->clazz->cpinfos[i]))->value);
+ break;
+ case CONSTANT_Double:
+ OP_LOADCONST_D(((constant_double *) (m->clazz->cpinfos[i]))->value);
+ break;
+ case CONSTANT_String:
+ OP_LOADCONST_STRING(literalstring_new((utf *) (m->clazz->cpinfos[i])));
+ break;
+ case CONSTANT_Class:
+ cr = (constant_classref *) (m->clazz->cpinfos[i]);
+
+ if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
+ return false;
+
+ /* if not resolved, c == NULL */
+
+ OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr);
+
+ break;
+
+#if defined(ENABLE_VERIFIER)
+ default:
+ exceptions_throw_verifyerror(m,
+ "Invalid constant type to push");
+ return false;
+#endif
+ }
+ break;
+
+ case BC_aconst_null:
+ OP_LOADCONST_NULL();
+ break;
+
+ case BC_iconst_m1:
+ case BC_iconst_0:
+ case BC_iconst_1:
+ case BC_iconst_2:
+ case BC_iconst_3:
+ case BC_iconst_4:
+ case BC_iconst_5:
+ OP_LOADCONST_I(opcode - BC_iconst_0);
+ break;
+
+ case BC_lconst_0:
+ case BC_lconst_1:
+ OP_LOADCONST_L(opcode - BC_lconst_0);
+ break;
+
+ case BC_fconst_0:
+ case BC_fconst_1:
+ case BC_fconst_2:
+ OP_LOADCONST_F(opcode - BC_fconst_0);
+ break;
+
+ case BC_dconst_0:
+ case BC_dconst_1:
+ OP_LOADCONST_D(opcode - BC_dconst_0);
+ break;
+
+ /* stack operations ***************************************************/
+
+ /* We need space for additional instruction so we can
+ translate these instructions to sequences of ICMD_COPY and
+ ICMD_MOVE instructions. */
+
+ case BC_dup_x1:
+ INSTRUCTIONS_CHECK(4);
+ OP(opcode);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ break;
+
+ case BC_dup_x2:
+ INSTRUCTIONS_CHECK(6);
+ OP(opcode);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ break;
+
+ case BC_dup2:
+ INSTRUCTIONS_CHECK(2);
+ OP(opcode);
+ OP(ICMD_NOP);
+ break;
+
+ case BC_dup2_x1:
+ INSTRUCTIONS_CHECK(7);
+ OP(opcode);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ break;
+
+ case BC_dup2_x2:
+ INSTRUCTIONS_CHECK(9);
+ OP(opcode);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ break;
+
+ case BC_swap:
+ INSTRUCTIONS_CHECK(3);
+ OP(opcode);
+ OP(ICMD_NOP);
+ OP(ICMD_NOP);
+ break;
+
+ /* local variable access instructions *********************************/
+
+ case BC_iload:
+ case BC_fload:
+ case BC_aload:
+ if (iswide == false) {
+ i = SUCK_BE_U1(m->jcode + bcindex + 1);
+ }
+ else {
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ nextbc = bcindex + 3;
+ iswide = false;
+ }
+ OP_LOAD_ONEWORD(opcode, i, opcode - BC_iload);
+ break;
+
+ case BC_lload:
+ case BC_dload:
+ if (iswide == false) {
+ i = SUCK_BE_U1(m->jcode + bcindex + 1);
+ }
+ else {
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ nextbc = bcindex + 3;
+ iswide = false;
+ }
+ OP_LOAD_TWOWORD(opcode, i, opcode - BC_iload);
+ break;
+
+ case BC_iload_0:
+ case BC_iload_1:
+ case BC_iload_2:
+ case BC_iload_3:
+ OP_LOAD_ONEWORD(ICMD_ILOAD, opcode - BC_iload_0, TYPE_INT);
+ break;
+
+ case BC_lload_0:
+ case BC_lload_1:
+ case BC_lload_2:
+ case BC_lload_3:
+ OP_LOAD_TWOWORD(ICMD_LLOAD, opcode - BC_lload_0, TYPE_LNG);
+ break;
+
+ case BC_fload_0:
+ case BC_fload_1:
+ case BC_fload_2:
+ case BC_fload_3:
+ OP_LOAD_ONEWORD(ICMD_FLOAD, opcode - BC_fload_0, TYPE_FLT);
+ break;
+
+ case BC_dload_0:
+ case BC_dload_1:
+ case BC_dload_2:
+ case BC_dload_3:
+ OP_LOAD_TWOWORD(ICMD_DLOAD, opcode - BC_dload_0, TYPE_DBL);
+ break;
+
+ case BC_aload_0:
+ case BC_aload_1:
+ case BC_aload_2:
+ case BC_aload_3:
+ OP_LOAD_ONEWORD(ICMD_ALOAD, opcode - BC_aload_0, TYPE_ADR);
+ break;
+
+ case BC_istore:
+ case BC_fstore:
+ case BC_astore:
+ if (iswide == false) {
+ i = SUCK_BE_U1(m->jcode + bcindex + 1);
+ }
+ else {
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ nextbc = bcindex + 3;
+ iswide = false;
+ }
+ OP_STORE_ONEWORD(opcode, i, opcode - BC_istore);
+ break;
+
+ case BC_lstore:
+ case BC_dstore:
+ if (iswide == false) {
+ i = SUCK_BE_U1(m->jcode + bcindex + 1);
+ }
+ else {
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ nextbc = bcindex + 3;
+ iswide = false;
+ }
+ OP_STORE_TWOWORD(opcode, i, opcode - BC_istore);
+ break;
+
+ case BC_istore_0:
+ case BC_istore_1:
+ case BC_istore_2:
+ case BC_istore_3:
+ OP_STORE_ONEWORD(ICMD_ISTORE, opcode - BC_istore_0, TYPE_INT);
+ break;
+
+ case BC_lstore_0:
+ case BC_lstore_1:
+ case BC_lstore_2:
+ case BC_lstore_3:
+ OP_STORE_TWOWORD(ICMD_LSTORE, opcode - BC_lstore_0, TYPE_LNG);
+ break;
+
+ case BC_fstore_0:
+ case BC_fstore_1:
+ case BC_fstore_2:
+ case BC_fstore_3:
+ OP_STORE_ONEWORD(ICMD_FSTORE, opcode - BC_fstore_0, TYPE_FLT);
+ break;
+
+ case BC_dstore_0:
+ case BC_dstore_1:
+ case BC_dstore_2:
+ case BC_dstore_3:
+ OP_STORE_TWOWORD(ICMD_DSTORE, opcode - BC_dstore_0, TYPE_DBL);
+ break;
+
+ case BC_astore_0:
+ case BC_astore_1:
+ case BC_astore_2:
+ case BC_astore_3:
+ OP_STORE_ONEWORD(ICMD_ASTORE, opcode - BC_astore_0, TYPE_ADR);
+ break;
+
+ case BC_iinc:
+ {
+ int v;
+
+ if (iswide == false) {
+ i = SUCK_BE_U1(m->jcode + bcindex + 1);
+ v = SUCK_BE_S1(m->jcode + bcindex + 2);
+ }
+ else {
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ v = SUCK_BE_S2(m->jcode + bcindex + 3);
+ nextbc = bcindex + 5;
+ iswide = false;
+ }
+ INDEX_ONEWORD(i);
+ LOCALTYPE_USED(i, TYPE_INT);
+ OP_LOCALINDEX_I(opcode, i, v);
+ }
+ break;
+
+ /* wider index for loading, storing and incrementing ******************/
+
+ case BC_wide:
+ bcindex++;
+ iswide = true;
+ goto fetch_opcode;
+
+ /* managing arrays ****************************************************/
+
+ case BC_newarray:
+ switch (SUCK_BE_S1(m->jcode + bcindex + 1)) {
+ case 4:
+ bte = builtintable_get_internal(BUILTIN_newarray_boolean);
+ break;
+ case 5:
+ bte = builtintable_get_internal(BUILTIN_newarray_char);
+ break;
+ case 6:
+ bte = builtintable_get_internal(BUILTIN_newarray_float);
+ break;
+ case 7:
+ bte = builtintable_get_internal(BUILTIN_newarray_double);
+ break;
+ case 8:
+ bte = builtintable_get_internal(BUILTIN_newarray_byte);
+ break;
+ case 9:
+ bte = builtintable_get_internal(BUILTIN_newarray_short);
+ break;
+ case 10:
+ bte = builtintable_get_internal(BUILTIN_newarray_int);
+ break;
+ case 11:
+ bte = builtintable_get_internal(BUILTIN_newarray_long);
+ break;
+#if defined(ENABLE_VERIFIER)
+ default:
+ exceptions_throw_verifyerror(m, "Invalid array-type to create");
+ return false;
+#endif
+ }
+ OP_BUILTIN_CHECK_EXCEPTION(bte);
+ break;
+
+ case BC_anewarray:
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ compr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class);
+ if (compr == NULL)
+ return false;
+
+ if (!(cr = class_get_classref_multiarray_of(1, compr)))
+ return false;
+
+ if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
+ return false;
+
+ INSTRUCTIONS_CHECK(2);
+ OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr);
+ bte = builtintable_get_internal(BUILTIN_newarray);
+ OP_BUILTIN_CHECK_EXCEPTION(bte);
+ s_count++;
+ break;
+
+ case BC_multianewarray:
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ j = SUCK_BE_U1(m->jcode + bcindex + 3);
+
+ cr = (constant_classref *) class_getconstant(m->clazz, i, CONSTANT_Class);
+ if (cr == NULL)
+ return false;
+
+ if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
+ return false;
+
+ /* if unresolved, c == NULL */
+
+ iptr->s1.argcount = j;
+ OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, INS_FLAG_CHECK);
+ code_unflag_leafmethod(code);
+ break;
+
+ /* control flow instructions ******************************************/
+
+ case BC_ifeq:
+ case BC_iflt:
+ case BC_ifle:
+ case BC_ifne:
+ case BC_ifgt:
+ case BC_ifge:
+ case BC_ifnull:
+ case BC_ifnonnull:
+ case BC_if_icmpeq:
+ case BC_if_icmpne:
+ case BC_if_icmplt:
+ case BC_if_icmpgt:
+ case BC_if_icmple:
+ case BC_if_icmpge:
+ case BC_if_acmpeq:
+ case BC_if_acmpne:
+ case BC_goto:
+ i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1);
+ CHECK_BYTECODE_INDEX(i);
+ MARK_BASICBLOCK(&pd, i);
+ blockend = true;
+ OP_INSINDEX(opcode, i);
+ break;
+
+ case BC_goto_w:
+ i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1);
+ CHECK_BYTECODE_INDEX(i);
+ MARK_BASICBLOCK(&pd, i);
+ blockend = true;
+ OP_INSINDEX(ICMD_GOTO, i);
+ break;
+
+ case BC_jsr:
+ i = bcindex + SUCK_BE_S2(m->jcode + bcindex + 1);
+jsr_tail:
+ CHECK_BYTECODE_INDEX(i);
+ MARK_BASICBLOCK(&pd, i);
+ blockend = true;
+ OP_PREPARE_ZEROFLAGS(BC_jsr);
+ iptr->sx.s23.s3.jsrtarget.insindex = i;
+ PINC;
+ break;
+
+ case BC_jsr_w:
+ i = bcindex + SUCK_BE_S4(m->jcode + bcindex + 1);
+ goto jsr_tail;
+
+ case BC_ret:
+ if (iswide == false) {
+ i = SUCK_BE_U1(m->jcode + bcindex + 1);
+ }
+ else {
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ nextbc = bcindex + 3;
+ iswide = false;
+ }
+ blockend = true;
+
+ OP_LOAD_ONEWORD(opcode, i, TYPE_ADR);
+ break;
+
+ case BC_ireturn:
+ case BC_lreturn:
+ case BC_freturn:
+ case BC_dreturn:
+ case BC_areturn:
+ case BC_return:
+ blockend = true;
+ /* XXX ARETURN will need a flag in the typechecker */
+ OP(opcode);
+ break;
+
+ case BC_athrow:
+ blockend = true;
+ /* XXX ATHROW will need a flag in the typechecker */
+ OP(opcode);
+ break;
+
+
+ /* table jumps ********************************************************/
+
+ case BC_lookupswitch:
+ {
+ s4 num, j;
+ lookup_target_t *lookup;
+#if defined(ENABLE_VERIFIER)
+ s4 prevvalue = 0;
+#endif
+ blockend = true;
+ nextbc = MEMORY_ALIGN((bcindex + 1), 4);
+
+ CHECK_END_OF_BYTECODE(nextbc + 8);
+
+ OP_PREPARE_ZEROFLAGS(opcode);
+
+ /* default target */
+
+ j = bcindex + SUCK_BE_S4(m->jcode + nextbc);
+ iptr->sx.s23.s3.lookupdefault.insindex = j;
+ nextbc += 4;
+ CHECK_BYTECODE_INDEX(j);
+ MARK_BASICBLOCK(&pd, j);
+
+ /* number of pairs */
+
+ num = SUCK_BE_U4(m->jcode + nextbc);
+ iptr->sx.s23.s2.lookupcount = num;
+ nextbc += 4;
+
+ /* allocate the intermediate code table */
+
+ lookup = (lookup_target_t*) DumpMemory::allocate(sizeof(lookup_target_t) * num);
+ iptr->dst.lookup = lookup;
+
+ /* iterate over the lookup table */
+
+ CHECK_END_OF_BYTECODE(nextbc + 8 * num);
+
+ for (i = 0; i < num; i++) {
+ /* value */
+
+ j = SUCK_BE_S4(m->jcode + nextbc);
+ lookup->value = j;
+
+ nextbc += 4;
+
+#if defined(ENABLE_VERIFIER)
+ /* check if the lookup table is sorted correctly */
+
+ if (i && (j <= prevvalue)) {
+ exceptions_throw_verifyerror(m, "Unsorted lookup switch");
+ return false;
+ }
+ prevvalue = j;
+#endif
+ /* target */
+
+ j = bcindex + SUCK_BE_S4(m->jcode + nextbc);
+ lookup->target.insindex = j;
+ lookup++;
+ nextbc += 4;
+ CHECK_BYTECODE_INDEX(j);
+ MARK_BASICBLOCK(&pd, j);
+ }
+
+ PINC;
+ break;
+ }
+
+ case BC_tableswitch:
+ {
+ s4 num, j;
+ s4 deftarget;
+ branch_target_t *table;
+
+ blockend = true;
+ nextbc = MEMORY_ALIGN((bcindex + 1), 4);
+
+ CHECK_END_OF_BYTECODE(nextbc + 12);
+
+ OP_PREPARE_ZEROFLAGS(opcode);
+
+ /* default target */
+
+ deftarget = bcindex + SUCK_BE_S4(m->jcode + nextbc);
+ nextbc += 4;
+ CHECK_BYTECODE_INDEX(deftarget);
+ MARK_BASICBLOCK(&pd, deftarget);
+
+ /* lower bound */
+
+ j = SUCK_BE_S4(m->jcode + nextbc);
+ iptr->sx.s23.s2.tablelow = j;
+ nextbc += 4;
+
+ /* upper bound */
+
+ num = SUCK_BE_S4(m->jcode + nextbc);
+ iptr->sx.s23.s3.tablehigh = num;
+ nextbc += 4;
+
+ /* calculate the number of table entries */
+
+ num = num - j + 1;
+
+#if defined(ENABLE_VERIFIER)
+ if (num < 1) {
+ exceptions_throw_verifyerror(m,
+ "invalid TABLESWITCH: upper bound < lower bound");
+ return false;
+ }
+#endif
+ /* create the intermediate code table */
+ /* the first entry is the default target */
+
+ table = (branch_target_t*) DumpMemory::allocate(sizeof(branch_target_t) * (1 + num));
+ iptr->dst.table = table;
+ (table++)->insindex = deftarget;
+
+ /* iterate over the target table */
+
+ CHECK_END_OF_BYTECODE(nextbc + 4 * num);
+
+ for (i = 0; i < num; i++) {
+ j = bcindex + SUCK_BE_S4(m->jcode + nextbc);
+ (table++)->insindex = j;
+ nextbc += 4;
+ CHECK_BYTECODE_INDEX(j);
+ MARK_BASICBLOCK(&pd, j);
+ }
+
+ PINC;
+ break;
+ }
+
+
+ /* load and store of object fields ************************************/
+
+ case BC_aastore:
+ OP(opcode);
+ code_unflag_leafmethod(code);
+ break;
+
+ case BC_getstatic:
+ case BC_putstatic:
+ case BC_getfield:
+ case BC_putfield:
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Fieldref);
+
+ if (fmi == NULL)
+ return false;
+
+ OP_PREPARE_ZEROFLAGS(opcode);
+ iptr->sx.s23.s3.fmiref = fmi;
+
+ /* only with -noverify, otherwise the typechecker does this */
+
+#if defined(ENABLE_VERIFIER)
+ if (!JITDATA_HAS_FLAG_VERIFY(jd)) {
+#endif
+ result = resolve_field_lazy(m, fmi);
+
+ if (result == resolveFailed)
+ return false;
+
+ if (result != resolveSucceeded) {
+ uf = resolve_create_unresolved_field(m->clazz, m, iptr);
+
+ if (uf == NULL)
+ return false;
+
+ /* store the unresolved_field pointer */
+
+ iptr->sx.s23.s3.uf = uf;
+ iptr->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
+#if defined(ENABLE_VERIFIER)
+ }
+#endif
+ PINC;
+ break;
+
+
+ /* method invocation **************************************************/
+
+ case BC_invokestatic:
+ OP_PREPARE_ZEROFLAGS(opcode);
+
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref);
+
+ if (fmi == NULL)
+ return false;
+
+ md = fmi->parseddesc.md;
+
+ if (md->params == NULL)
+ if (!descriptor_params_from_paramtypes(md, ACC_STATIC))
+ return false;
+
+ goto invoke_method;
+
+ case BC_invokespecial:
+ OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK);
+
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref);
+
+ goto invoke_nonstatic_method;
+
+ case BC_invokeinterface:
+ OP_PREPARE_ZEROFLAGS(opcode);
+
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_InterfaceMethodref);
+
+ goto invoke_nonstatic_method;
+
+ case BC_invokevirtual:
+ OP_PREPARE_ZEROFLAGS(opcode);
+
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ fmi = (constant_FMIref*) class_getconstant(m->clazz, i, CONSTANT_Methodref);
+
+invoke_nonstatic_method:
+ if (fmi == NULL)
+ return false;
+
+ md = fmi->parseddesc.md;
+
+ if (md->params == NULL)
+ if (!descriptor_params_from_paramtypes(md, 0))
+ return false;
+
+invoke_method:
+ code_unflag_leafmethod(code);
+
+ iptr->sx.s23.s3.fmiref = fmi;
+
+ /* only with -noverify, otherwise the typechecker does this */
+
+#if defined(ENABLE_VERIFIER)
+ if (!JITDATA_HAS_FLAG_VERIFY(jd)) {
+#endif
+ result = resolve_method_lazy(m, fmi,
+ (opcode == BC_invokespecial));
+
+ if (result == resolveFailed)
+ return false;
+
+ if (result == resolveSucceeded) {
+ methodinfo *mi = iptr->sx.s23.s3.fmiref->p.method;
+
+ /* if this call is monomorphic, turn it into an
+ INVOKESPECIAL */
+
+ assert(IS_FMIREF_RESOLVED(iptr->sx.s23.s3.fmiref));
+
+ if ((iptr->opc == ICMD_INVOKEVIRTUAL)
+ && (mi->flags & (ACC_FINAL | ACC_PRIVATE)))
+ {
+ iptr->opc = ICMD_INVOKESPECIAL;
+ iptr->flags.bits |= INS_FLAG_CHECK;
+ }
+ }
+ else {
+ um = resolve_create_unresolved_method(m->clazz, m, fmi,
+ (opcode == BC_invokestatic),
+ (opcode == BC_invokespecial));
+
+ if (um == NULL)
+ return false;
+
+ /* store the unresolved_method pointer */
+
+ iptr->sx.s23.s3.um = um;
+ iptr->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
+#if defined(ENABLE_VERIFIER)
+ }
+#endif
+ PINC;
+ break;
+
+ /* instructions taking class arguments ********************************/
+
+ case BC_new:
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class);
+
+ if (cr == NULL)
+ return false;
+
+ if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
+ return false;
+
+ INSTRUCTIONS_CHECK(2);
+ OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr);
+ bte = builtintable_get_internal(BUILTIN_new);
+ OP_BUILTIN_CHECK_EXCEPTION(bte);
+ s_count++;
+ break;
+
+ case BC_checkcast:
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class);
+
+ if (cr == NULL)
+ return false;
+
+ if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
+ return false;
+
+ if (cr->name->text[0] == '[') {
+ /* array type cast-check */
+ flags = INS_FLAG_CHECK | INS_FLAG_ARRAY;
+ code_unflag_leafmethod(code);
+ }
+ else {
+ /* object type cast-check */
+ flags = INS_FLAG_CHECK;
+ }
+ OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, flags);
+ break;
+
+ case BC_instanceof:
+ i = SUCK_BE_U2(m->jcode + bcindex + 1);
+ cr = (constant_classref*) class_getconstant(m->clazz, i, CONSTANT_Class);
+
+ if (cr == NULL)
+ return false;
+
+ if (!resolve_classref(m, cr, resolveLazy, true, true, &c))
+ return false;
+
+ if (cr->name->text[0] == '[') {
+ /* array type cast-check */
+ INSTRUCTIONS_CHECK(2);
+ OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr);
+ bte = builtintable_get_internal(BUILTIN_arrayinstanceof);
+ OP_BUILTIN_NO_EXCEPTION(bte);
+ s_count++;
+ }
+ else {
+ /* object type cast-check */
+ OP_S3_CLASSINFO_OR_CLASSREF(opcode, c, cr, 0 /* flags*/);
+ }
+ break;
+
+ /* synchronization instructions ***************************************/
+
+ case BC_monitorenter:
+#if defined(ENABLE_THREADS)
+ if (checksync) {
+ bte = builtintable_get_internal(LOCK_monitor_enter);
+ OP_BUILTIN_CHECK_EXCEPTION(bte);
+ }
+ else
+#endif
+ {
+ OP_CHECK_EXCEPTION(ICMD_CHECKNULL);
+ OP(ICMD_POP);
+ }
+ break;
+
+ case BC_monitorexit:
+#if defined(ENABLE_THREADS)
+ if (checksync) {
+ bte = builtintable_get_internal(LOCK_monitor_exit);
+ OP_BUILTIN_CHECK_EXCEPTION(bte);
+ }
+ else
+#endif
+ {
+ OP_CHECK_EXCEPTION(ICMD_CHECKNULL);
+ OP(ICMD_POP);
+ }
+ break;
+
+ /* arithmetic instructions that may become builtin functions **********/
+
+ case BC_idiv:
+#if !SUPPORT_DIVISION
+ bte = builtintable_get_internal(BUILTIN_idiv);
+ OP_BUILTIN_ARITHMETIC(opcode, bte);
+#else
+# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
+ OP(opcode);
+# else
+ OP_CHECK_EXCEPTION(opcode);
+# endif
+#endif
+ break;
+
+ case BC_irem:
+#if !SUPPORT_DIVISION
+ bte = builtintable_get_internal(BUILTIN_irem);
+ OP_BUILTIN_ARITHMETIC(opcode, bte);
+#else
+# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
+ OP(opcode);
+# else
+ OP_CHECK_EXCEPTION(opcode);
+# endif
+#endif
+ break;
+
+ case BC_ldiv:
+#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV)
+ bte = builtintable_get_internal(BUILTIN_ldiv);
+ OP_BUILTIN_ARITHMETIC(opcode, bte);
+#else
+# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
+ OP(opcode);
+# else
+ OP_CHECK_EXCEPTION(opcode);
+# endif
+#endif
+ break;
+
+ case BC_lrem:
+#if !(SUPPORT_DIVISION && SUPPORT_LONG && SUPPORT_LONG_DIV)
+ bte = builtintable_get_internal(BUILTIN_lrem);
+ OP_BUILTIN_ARITHMETIC(opcode, bte);
+#else
+# if SUPPORT_HARDWARE_DIVIDE_BY_ZERO
+ OP(opcode);
+# else
+ OP_CHECK_EXCEPTION(opcode);
+# endif
+#endif
+ break;
+
+ case BC_frem:
+#if defined(__I386__)
+ OP(opcode);
+#else
+ bte = builtintable_get_internal(BUILTIN_frem);
+ OP_BUILTIN_NO_EXCEPTION(bte);
+#endif
+ break;
+
+ case BC_drem:
+#if defined(__I386__)
+ OP(opcode);
+#else
+ bte = builtintable_get_internal(BUILTIN_drem);
+ OP_BUILTIN_NO_EXCEPTION(bte);
+#endif
+ break;
+
+ case BC_f2i:
+#if defined(__ALPHA__)
+ bte = builtintable_get_internal(BUILTIN_f2i);
+ OP_BUILTIN_NO_EXCEPTION(bte);
+#else
+ OP(opcode);
+#endif
+ break;
+
+ case BC_f2l:
+#if defined(__ALPHA__)
+ bte = builtintable_get_internal(BUILTIN_f2l);
+ OP_BUILTIN_NO_EXCEPTION(bte);
+#else
+ OP(opcode);
+#endif
+ break;
+
+ case BC_d2i:
+#if defined(__ALPHA__)
+ bte = builtintable_get_internal(BUILTIN_d2i);
+ OP_BUILTIN_NO_EXCEPTION(bte);
+#else
+ OP(opcode);
+#endif
+ break;
+
+ case BC_d2l:
+#if defined(__ALPHA__)
+ bte = builtintable_get_internal(BUILTIN_d2l);
+ OP_BUILTIN_NO_EXCEPTION(bte);
+#else
+ OP(opcode);
+#endif
+ break;
+
+
+ /* invalid opcodes ****************************************************/
+
+ /* check for invalid opcodes if the verifier is enabled */
+#if defined(ENABLE_VERIFIER)
+ case BC_breakpoint:
+ exceptions_throw_verifyerror(m, "Quick instructions shouldn't appear, yet.");
+ return false;
+
+
+ /* Unused opcodes ************************************************** */
+
+ case 186:
+ case 203:
+ case 204:
+ case 205:
+ case 206:
+ case 207:
+ case 208:
+ case 209:
+ case 210:
+ case 211:
+ case 212:
+ case 213:
+ case 214:
+ case 215:
+ case 216:
+ case 217:
+ case 218:
+ case 219:
+ case 220:
+ case 221:
+ case 222:
+ case 223:
+ case 224:
+ case 225:
+ case 226:
+ case 227:
+ case 228:
+ case 229:
+ case 230:
+ case 231:
+ case 232:
+ case 233:
+ case 234:
+ case 235:
+ case 236:
+ case 237:
+ case 238:
+ case 239:
+ case 240:
+ case 241:
+ case 242:
+ case 243:
+ case 244:
+ case 245:
+ case 246:
+ case 247:
+ case 248:
+ case 249:
+ case 250:
+ case 251:
+ case 252:
+ case 253:
+ case 254:
+ case 255:
+ exceptions_throw_verifyerror(m, "Illegal opcode %d at instr %d\n",
+ opcode, ircount);
+ return false;
+ break;
+#endif /* defined(ENABLE_VERIFIER) */
+
+ /* opcodes that don't require translation *****************************/
+
+ default:
+ /* Straight-forward translation to HIR. */
+ OP(opcode);
+ break;
+
+ } /* end switch */
+
+ /* verifier checks ****************************************************/
+
+#if defined(ENABLE_VERIFIER)
+ /* If WIDE was used correctly, iswide should have been reset by now. */
+ if (iswide) {
+ exceptions_throw_verifyerror(m,
+ "Illegal instruction: WIDE before incompatible opcode");
+ return false;
+ }
+#endif /* defined(ENABLE_VERIFIER) */
+
+ } /* end for */
+
+ if (JITDATA_HAS_FLAG_REORDER(jd)) {
+ /* add a NOP to the last basic block */
+
+ INSTRUCTIONS_CHECK(1);
+ OP(ICMD_NOP);
+ }
+
+ /*** END OF LOOP **********************************************************/
+
+ /* assert that we did not write more ICMDs than allocated */
+
+ assert(ircount <= pd.instructionslength);
+ assert(ircount == (iptr - pd.instructions));
+
+ /*** verifier checks ******************************************************/
+
+#if defined(ENABLE_VERIFIER)
+ if (bcindex != m->jcodelength) {
+ exceptions_throw_verifyerror(m,
+ "Command-sequence crosses code-boundary");
+ return false;
+ }
+
+ if (!blockend) {
+ exceptions_throw_verifyerror(m, "Falling off the end of the code");
+ return false;
+ }
+#endif /* defined(ENABLE_VERIFIER) */
+
+ /*** setup the methodinfo, allocate stack and basic blocks ****************/
+
+ /* identify basic blocks */
+
+ /* check if first instruction is a branch target */
+
+ if (pd.basicblockstart[0] == 1) {
+ jd->branchtoentry = true;
+ }
+ else {
+ /* first instruction always starts a basic block */
+
+ iptr = pd.instructions;
+
+ iptr->flags.bits |= INS_FLAG_BASICBLOCK;
+ }
+
+ /* Iterate over all bytecode instructions and set missing
+ basic-block starts in IR instructions. */
+
+ for (bcindex = 0; bcindex < m->jcodelength; bcindex++) {
+ /* Does the current bytecode instruction start a basic
+ block? */
+
+ if (pd.basicblockstart[bcindex] == 1) {
+#if defined(ENABLE_VERIFIER)
+ /* Check if this bytecode basic-block start at the
+ beginning of a bytecode instruction. */
+
+ if (pd.bytecodestart[bcindex] == 0) {
+ exceptions_throw_verifyerror(m,
+ "Branch into middle of instruction");
+ return false;
+ }
+#endif
+
+ /* Get the IR instruction mapped to the bytecode
+ instruction and set the basic block flag. */
+
+ irindex = pd.bytecodemap[bcindex];
+ iptr = pd.instructions + irindex;
+
+ iptr->flags.bits |= INS_FLAG_BASICBLOCK;
+ }
+ }
+
+ /* IR instruction index to basic-block index mapping */
+
+ pd.instructionmap = (s4*) DumpMemory::allocate(sizeof(s4) * ircount);
+ MZERO(pd.instructionmap, s4, ircount);
+
+ /* Iterate over all IR instructions and count the basic blocks. */
+
+ iptr = pd.instructions;
+
+ bbcount = 0;
+
+ for (i = 0; i < ircount; i++, iptr++) {
+ if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) {
+ /* store the basic-block number in the IR instruction
+ map */
+
+ pd.instructionmap[i] = bbcount;
+
+ /* post-increment the basic-block count */
+
+ bbcount++;
+ }
+ }
+
+ /* Allocate basic block array (one more for end ipc). */
+
+ jd->basicblocks = (basicblock*) DumpMemory::allocate(sizeof(basicblock) * (bbcount + 1));
+ MZERO(jd->basicblocks, basicblock, bbcount + 1);
+
+ /* Now iterate again over all IR instructions and initialize the
+ basic block structures and, in the same loop, resolve the
+ branch-target instruction indices to basic blocks. */
+
+ iptr = pd.instructions;
+ bptr = jd->basicblocks;
+
+ bbcount = 0;
+
+ for (i = 0; i < ircount; i++, iptr++) {
+ /* check for basic block */
+
+ if (INSTRUCTION_STARTS_BASICBLOCK(iptr)) {
+ /* intialize the basic block */
+
+ BASICBLOCK_INIT(bptr, m);
+
+ bptr->iinstr = iptr;
+
+ if (bbcount > 0) {
+ bptr[-1].icount = bptr->iinstr - bptr[-1].iinstr;
+ }
+
+ /* bptr->icount is set when the next block is allocated */
+
+ bptr->nr = bbcount++;
+ bptr++;
+ bptr[-1].next = bptr;
+ }
+
+ /* resolve instruction indices to basic blocks */
+
+ switch (iptr->opc) {
+ case ICMD_IFEQ:
+ case ICMD_IFLT:
+ case ICMD_IFLE:
+ case ICMD_IFNE:
+ case ICMD_IFGT:
+ case ICMD_IFGE:
+ case ICMD_IFNULL:
+ case ICMD_IFNONNULL:
+ case ICMD_IF_ICMPEQ:
+ case ICMD_IF_ICMPNE:
+ case ICMD_IF_ICMPLT:
+ case ICMD_IF_ICMPGT:
+ case ICMD_IF_ICMPLE:
+ case ICMD_IF_ICMPGE:
+ case ICMD_IF_ACMPEQ:
+ case ICMD_IF_ACMPNE:
+ case ICMD_GOTO:
+ BYTECODEINDEX_TO_BASICBLOCK(iptr->dst);
+ break;
+
+ case ICMD_JSR:
+ BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.jsrtarget);
+ break;
+
+ case ICMD_TABLESWITCH:
+ table = iptr->dst.table;
+
+ BYTECODEINDEX_TO_BASICBLOCK(*table);
+ table++;
+
+ j = iptr->sx.s23.s3.tablehigh - iptr->sx.s23.s2.tablelow + 1;
+
+ while (--j >= 0) {
+ BYTECODEINDEX_TO_BASICBLOCK(*table);
+ table++;
+ }
+ break;
+
+ case ICMD_LOOKUPSWITCH:
+ BYTECODEINDEX_TO_BASICBLOCK(iptr->sx.s23.s3.lookupdefault);
+
+ lookup = iptr->dst.lookup;
+
+ j = iptr->sx.s23.s2.lookupcount;
+
+ while (--j >= 0) {
+ BYTECODEINDEX_TO_BASICBLOCK(lookup->target);
+ lookup++;
+ }
+ break;
+ }
+ }
+
+ /* set instruction count of last real block */
+
+ if (bbcount > 0) {
+ bptr[-1].icount = (pd.instructions + ircount) - bptr[-1].iinstr;
+ }
+
+ /* allocate additional block at end */
+
+ BASICBLOCK_INIT(bptr, m);
+ bptr->nr = bbcount;
+
+ /* set basicblock pointers in exception table */
+
+ if (!parse_resolve_exception_table(jd, &pd))
+ return false;
+
+ /* store the local map */
+
+ jd->local_map = local_map;
+
+ /* calculate local variable renaming */
+
+ {
+ s4 nlocals = 0;
+ s4 i;
+ s4 t;
+ s4 varindex;
+ s4 *mapptr;
+ s4 *reversemap;
+
+ mapptr = local_map;
+
+ /* iterate over local_map[0..m->maxlocals*5-1] and allocate a unique */
+ /* variable index for each _used_ (javaindex,type) pair. */
+ /* (local_map[javaindex*5+type] = cacaoindex) */
+ /* Unused (javaindex,type) pairs are marked with UNUSED. */
+
+ for (i = 0; i < (m->maxlocals * 5); i++, mapptr++) {
+ if (*mapptr)
+ *mapptr = nlocals++;
+ else
+ *mapptr = UNUSED;
+ }
+
+ jd->localcount = nlocals;
+
+ /* calculate the (maximum) number of variables needed */
+
+ jd->varcount =
+ nlocals /* local variables */
+ + bbcount * m->maxstack /* invars */
+ + s_count; /* variables created within blocks (non-invar) */
+
+ /* reserve the first indices for local variables */
+
+ jd->vartop = nlocals;
+
+ /* reserve extra variables needed by stack analyse */
+
+ jd->varcount += STACK_EXTRA_VARS;
+ jd->vartop += STACK_EXTRA_VARS;
+
+ /* The verifier needs space for saving invars in some cases and */
+ /* extra variables. */
+
+#if defined(ENABLE_VERIFIER)
+ jd->varcount += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack;
+ jd->vartop += VERIFIER_EXTRA_LOCALS + VERIFIER_EXTRA_VARS + m->maxstack;
+#endif
+ /* allocate and initialize the variable array */
+
+ jd->var = (varinfo*) DumpMemory::allocate(sizeof(varinfo) * jd->varcount);
+ MZERO(jd->var, varinfo, jd->varcount);
+
+ /* set types of all locals in jd->var */
+ /* and fill the reverselocalmap */
+
+ reversemap = (s4*) DumpMemory::allocate(sizeof(s4) * nlocals);
+
+ for (i = 0; i < m->maxlocals; i++)
+ for (t=0; t<5; t++) {
+ varindex = local_map[5*i + t];
+ if (varindex != UNUSED) {
+ VAR(varindex)->type = t;
+ reversemap[varindex] = i;
+ }
+ }
+
+ jd->reverselocalmap = reversemap;
+ }
+
+ /* assign local variables to method variables */
+
+ jd->instructions = pd.instructions;
+ jd->instructioncount = ircount;
+ jd->basicblockcount = bbcount;
+ jd->stackcount = s_count + bbcount * m->maxstack; /* in-stacks */
+
+ /* allocate stack table */
+
+ jd->stack = (stackelement_t*) DumpMemory::allocate(sizeof(stackelement_t) * jd->stackcount);
+
+ /* everything's ok */
+
+ return true;
+
+ /*** goto labels for throwing verifier exceptions *************************/
+
+#if defined(ENABLE_VERIFIER)
+
+throw_unexpected_end_of_bytecode:
+ exceptions_throw_verifyerror(m, "Unexpected end of bytecode");
+ return false;
+
+throw_invalid_bytecode_index:
+ exceptions_throw_verifyerror(m, "Illegal target of branch instruction");
+ return false;
+
+throw_illegal_local_variable_number:
+ exceptions_throw_verifyerror(m, "Illegal local variable number");
+ return false;
+
+#endif /* ENABLE_VERIFIER */
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/parse.h - parser header
-
- Copyright (C) 1996-2005, 2006, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _PARSE_H
-#define _PARSE_H
-
-#include "config.h"
-#include "vm/types.h"
-
-#include "vm/global.h"
-#include "vm/jit/codegen-common.hpp"
-
-
-/* macros for verifier checks during parsing **********************************/
-
-#if defined(ENABLE_VERIFIER)
-
-/* We have to check local variables indices here because they are */
-/* used in stack.c to index the locals array. */
-
-#define INDEX_ONEWORD(num) \
- do { \
- if (((num) < 0) || ((num) >= m->maxlocals)) \
- goto throw_illegal_local_variable_number; \
- } while (0)
-
-#define INDEX_TWOWORD(num) \
- do { \
- if (((num) < 0) || (((num) + 1) >= m->maxlocals)) \
- goto throw_illegal_local_variable_number; \
- } while (0)
-
-/* CHECK_BYTECODE_INDEX(i) checks whether i is a valid bytecode index. */
-/* The end of the bytecode (i == m->jcodelength) is considered valid. */
-
-#define CHECK_BYTECODE_INDEX(i) \
- do { \
- if (((i) < 0) || ((i) >= m->jcodelength)) \
- goto throw_invalid_bytecode_index; \
- } while (0)
-
-/* CHECK_BYTECODE_INDEX_EXCLUSIVE is used for the exclusive ends */
-/* of exception handler ranges. */
-#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i) \
- do { \
- if ((i) < 0 || (i) > m->jcodelength) \
- goto throw_invalid_bytecode_index; \
- } while (0)
-
-#else /* !defined(ENABLE_VERIFIER) */
-
-#define INDEX_ONEWORD(num)
-#define INDEX_TWOWORD(num)
-#define CHECK_BYTECODE_INDEX(i)
-#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i)
-
-#endif /* defined(ENABLE_VERIFIER) */
-
-
-/* basic block generating macro ***********************************************/
-
-#define MARK_BASICBLOCK(pd, i) \
- do { \
- (pd)->basicblockstart[(i)] = 1; \
- } while (0)
-
-#define INSTRUCTIONS_CHECK(i) \
- if ((ircount + (i)) > pd.instructionslength) \
- iptr = parse_realloc_instructions(&pd, ircount, (i))
-
-
-/* intermediate code generating macros ****************************************/
-
-/* These macros ALWAYS set the following fields of *iptr to valid values: */
-/* iptr->opc */
-/* iptr->flags */
-/* iptr->line */
-
-/* These macros do NOT touch the following fields of *iptr, unless a value is */
-/* given for them: */
-/* iptr->s1 */
-/* iptr->sx */
-/* iptr->dst */
-
-/* The _PREPARE macros omit the PINC, so you can set additional fields */
-/* afterwards. */
-
-#define PINC \
- iptr++; ircount++
-
-#define OP_PREPARE_FLAGS(o, f) \
- iptr->opc = (o); \
- iptr->line = currentline; \
- iptr->flags.bits |= (f) | (ircount << INS_FLAG_ID_SHIFT);
-
-#define OP_PREPARE_ZEROFLAGS(o) \
- OP_PREPARE_FLAGS(o, 0)
-
-#define OP_PREPARE(o) \
- OP_PREPARE_ZEROFLAGS(o)
-
-#define OP(o) \
- OP_PREPARE_ZEROFLAGS(o); \
- PINC
-
-#define OP_CHECK_EXCEPTION(o) \
- OP_PREPARE_FLAGS(o, INS_FLAG_CHECK); \
- PINC
-
-#define OP_LOADCONST_I(v) \
- OP_PREPARE_ZEROFLAGS(ICMD_ICONST); \
- iptr->sx.val.i = (v); \
- PINC
-
-#define OP_LOADCONST_L(v) \
- OP_PREPARE_ZEROFLAGS(ICMD_LCONST); \
- iptr->sx.val.l = (v); \
- PINC
-
-#define OP_LOADCONST_F(v) \
- OP_PREPARE_ZEROFLAGS(ICMD_FCONST); \
- iptr->sx.val.f = (v); \
- PINC
-
-#define OP_LOADCONST_D(v) \
- OP_PREPARE_ZEROFLAGS(ICMD_DCONST); \
- iptr->sx.val.d = (v); \
- PINC
-
-#define OP_LOADCONST_NULL() \
- OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \
- iptr->sx.val.anyptr = NULL; \
- PINC
-
-#define OP_LOADCONST_STRING(v) \
- OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \
- iptr->sx.val.stringconst = (v); \
- PINC
-
-#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS(cl, cr, extraflags) \
- OP_PREPARE(ICMD_ACONST); \
- if (cl) { \
- iptr->sx.val.c.cls = (cl); \
- iptr->flags.bits |= INS_FLAG_CLASS | (extraflags); \
- } \
- else { \
- iptr->sx.val.c.ref = (cr); \
- iptr->flags.bits |= INS_FLAG_CLASS | INS_FLAG_UNRESOLVED \
- | (extraflags); \
- } \
- PINC
-
-#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr) \
- OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), INS_FLAG_CHECK)
-
-#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr) \
- OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), 0)
-
-#define OP_S3_CLASSINFO_OR_CLASSREF(o, c, cr, extraflags) \
- OP_PREPARE(o); \
- if (c) { \
- iptr->sx.s23.s3.c.cls= (c); \
- iptr->flags.bits |= (extraflags); \
- } \
- else { \
- iptr->sx.s23.s3.c.ref= (cr); \
- iptr->flags.bits |= INS_FLAG_UNRESOLVED | (extraflags); \
- } \
- PINC
-
-#define OP_INSINDEX(o, iindex) \
- OP_PREPARE_ZEROFLAGS(o); \
- iptr->dst.insindex = (iindex); \
- PINC
-
-# define OP_LOCALINDEX(o,index) \
- OP_PREPARE_ZEROFLAGS(o); \
- iptr->s1.varindex = (index); \
- PINC
-
-# define OP_LOCALINDEX_I(o,index,v) \
- OP_PREPARE_ZEROFLAGS(o); \
- iptr->s1.varindex = (index); \
- iptr->sx.val.i = (v); \
- PINC
-
-# define LOCALTYPE_USED(index,type) \
- do { \
- local_map[(index) * 5 + (type)] = 1; \
- } while (0)
-
-#define OP_LOAD_ONEWORD(o,index,type) \
- do { \
- INDEX_ONEWORD(index); \
- OP_LOCALINDEX(o,index); \
- LOCALTYPE_USED(index,type); \
- } while (0)
-
-#define OP_LOAD_TWOWORD(o,index,type) \
- do { \
- INDEX_TWOWORD(index); \
- OP_LOCALINDEX(o,index); \
- LOCALTYPE_USED(index,type); \
- } while (0)
-
-# define OP_STORE_ONEWORD(o,index,type) \
- do { \
- INDEX_ONEWORD(index); \
- OP_PREPARE_ZEROFLAGS(o); \
- iptr->dst.varindex = (index); \
- LOCALTYPE_USED(index,type); \
- PINC; \
- } while (0)
-
-# define OP_STORE_TWOWORD(o,index,type) \
- do { \
- INDEX_TWOWORD(index); \
- OP_PREPARE_ZEROFLAGS(o); \
- iptr->dst.varindex = (index); \
- LOCALTYPE_USED(index,type); \
- PINC; \
- } while (0)
-
-#define OP_BUILTIN_CHECK_EXCEPTION(bte) \
- code_unflag_leafmethod(code); \
- OP_PREPARE_FLAGS(ICMD_BUILTIN, INS_FLAG_CHECK); \
- iptr->sx.s23.s3.bte = (bte); \
- PINC
-
-#define OP_BUILTIN_NO_EXCEPTION(bte) \
- code_unflag_leafmethod(code); \
- OP_PREPARE_ZEROFLAGS(ICMD_BUILTIN); \
- iptr->sx.s23.s3.bte = (bte); \
- PINC
-
-#define OP_BUILTIN_ARITHMETIC(opcode, bte) \
- code_unflag_leafmethod(code); \
- OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK); \
- iptr->sx.s23.s3.bte = (bte); \
- PINC
-
-/* CAUTION: You must set iptr->flags yourself when using this! */
-#define OP_FMIREF_PREPARE(o, fmiref) \
- OP_PREPARE(o); \
- iptr->sx.s23.s3.fmiref = (fmiref);
-
-
-/* function prototypes ********************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-bool parse(jitdata *jd);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _PARSE_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
-
--- /dev/null
+/* src/vm/jit/parse.h - parser header
+
+ Copyright (C) 1996-2005, 2006, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _PARSE_H
+#define _PARSE_H
+
+#include "config.h"
+#include "vm/types.h"
+
+#include "vm/global.h"
+#include "vm/jit/codegen-common.hpp"
+
+
+/* macros for verifier checks during parsing **********************************/
+
+#if defined(ENABLE_VERIFIER)
+
+/* We have to check local variables indices here because they are */
+/* used in stack.c to index the locals array. */
+
+#define INDEX_ONEWORD(num) \
+ do { \
+ if (((num) < 0) || ((num) >= m->maxlocals)) \
+ goto throw_illegal_local_variable_number; \
+ } while (0)
+
+#define INDEX_TWOWORD(num) \
+ do { \
+ if (((num) < 0) || (((num) + 1) >= m->maxlocals)) \
+ goto throw_illegal_local_variable_number; \
+ } while (0)
+
+/* CHECK_BYTECODE_INDEX(i) checks whether i is a valid bytecode index. */
+/* The end of the bytecode (i == m->jcodelength) is considered valid. */
+
+#define CHECK_BYTECODE_INDEX(i) \
+ do { \
+ if (((i) < 0) || ((i) >= m->jcodelength)) \
+ goto throw_invalid_bytecode_index; \
+ } while (0)
+
+/* CHECK_BYTECODE_INDEX_EXCLUSIVE is used for the exclusive ends */
+/* of exception handler ranges. */
+#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i) \
+ do { \
+ if ((i) < 0 || (i) > m->jcodelength) \
+ goto throw_invalid_bytecode_index; \
+ } while (0)
+
+#else /* !defined(ENABLE_VERIFIER) */
+
+#define INDEX_ONEWORD(num)
+#define INDEX_TWOWORD(num)
+#define CHECK_BYTECODE_INDEX(i)
+#define CHECK_BYTECODE_INDEX_EXCLUSIVE(i)
+
+#endif /* defined(ENABLE_VERIFIER) */
+
+
+/* basic block generating macro ***********************************************/
+
+#define MARK_BASICBLOCK(pd, i) \
+ do { \
+ (pd)->basicblockstart[(i)] = 1; \
+ } while (0)
+
+#define INSTRUCTIONS_CHECK(i) \
+ if ((ircount + (i)) > pd.instructionslength) \
+ iptr = parse_realloc_instructions(&pd, ircount, (i))
+
+
+/* intermediate code generating macros ****************************************/
+
+/* These macros ALWAYS set the following fields of *iptr to valid values: */
+/* iptr->opc */
+/* iptr->flags */
+/* iptr->line */
+
+/* These macros do NOT touch the following fields of *iptr, unless a value is */
+/* given for them: */
+/* iptr->s1 */
+/* iptr->sx */
+/* iptr->dst */
+
+/* The _PREPARE macros omit the PINC, so you can set additional fields */
+/* afterwards. */
+
+#define PINC \
+ iptr++; ircount++
+
+#define OP_PREPARE_FLAGS(o, f) \
+ iptr->opc = (o); \
+ iptr->line = currentline; \
+ iptr->flags.bits |= (f) | (ircount << INS_FLAG_ID_SHIFT);
+
+#define OP_PREPARE_ZEROFLAGS(o) \
+ OP_PREPARE_FLAGS(o, 0)
+
+#define OP_PREPARE(o) \
+ OP_PREPARE_ZEROFLAGS(o)
+
+#define OP(o) \
+ OP_PREPARE_ZEROFLAGS(o); \
+ PINC
+
+#define OP_CHECK_EXCEPTION(o) \
+ OP_PREPARE_FLAGS(o, INS_FLAG_CHECK); \
+ PINC
+
+#define OP_LOADCONST_I(v) \
+ OP_PREPARE_ZEROFLAGS(ICMD_ICONST); \
+ iptr->sx.val.i = (v); \
+ PINC
+
+#define OP_LOADCONST_L(v) \
+ OP_PREPARE_ZEROFLAGS(ICMD_LCONST); \
+ iptr->sx.val.l = (v); \
+ PINC
+
+#define OP_LOADCONST_F(v) \
+ OP_PREPARE_ZEROFLAGS(ICMD_FCONST); \
+ iptr->sx.val.f = (v); \
+ PINC
+
+#define OP_LOADCONST_D(v) \
+ OP_PREPARE_ZEROFLAGS(ICMD_DCONST); \
+ iptr->sx.val.d = (v); \
+ PINC
+
+#define OP_LOADCONST_NULL() \
+ OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \
+ iptr->sx.val.anyptr = NULL; \
+ PINC
+
+#define OP_LOADCONST_STRING(v) \
+ OP_PREPARE_FLAGS(ICMD_ACONST, INS_FLAG_CHECK); \
+ iptr->sx.val.stringconst = (v); \
+ PINC
+
+#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS(cl, cr, extraflags) \
+ OP_PREPARE(ICMD_ACONST); \
+ if (cl) { \
+ iptr->sx.val.c.cls = (cl); \
+ iptr->flags.bits |= INS_FLAG_CLASS | (extraflags); \
+ } \
+ else { \
+ iptr->sx.val.c.ref = (cr); \
+ iptr->flags.bits |= INS_FLAG_CLASS | INS_FLAG_UNRESOLVED \
+ | (extraflags); \
+ } \
+ PINC
+
+#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_CHECK(c, cr) \
+ OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), INS_FLAG_CHECK)
+
+#define OP_LOADCONST_CLASSINFO_OR_CLASSREF_NOCHECK(c, cr) \
+ OP_LOADCONST_CLASSINFO_OR_CLASSREF_FLAGS((c), (cr), 0)
+
+#define OP_S3_CLASSINFO_OR_CLASSREF(o, c, cr, extraflags) \
+ OP_PREPARE(o); \
+ if (c) { \
+ iptr->sx.s23.s3.c.cls= (c); \
+ iptr->flags.bits |= (extraflags); \
+ } \
+ else { \
+ iptr->sx.s23.s3.c.ref= (cr); \
+ iptr->flags.bits |= INS_FLAG_UNRESOLVED | (extraflags); \
+ } \
+ PINC
+
+#define OP_INSINDEX(o, iindex) \
+ OP_PREPARE_ZEROFLAGS(o); \
+ iptr->dst.insindex = (iindex); \
+ PINC
+
+# define OP_LOCALINDEX(o,index) \
+ OP_PREPARE_ZEROFLAGS(o); \
+ iptr->s1.varindex = (index); \
+ PINC
+
+# define OP_LOCALINDEX_I(o,index,v) \
+ OP_PREPARE_ZEROFLAGS(o); \
+ iptr->s1.varindex = (index); \
+ iptr->sx.val.i = (v); \
+ PINC
+
+# define LOCALTYPE_USED(index,type) \
+ do { \
+ local_map[(index) * 5 + (type)] = 1; \
+ } while (0)
+
+#define OP_LOAD_ONEWORD(o,index,type) \
+ do { \
+ INDEX_ONEWORD(index); \
+ OP_LOCALINDEX(o,index); \
+ LOCALTYPE_USED(index,type); \
+ } while (0)
+
+#define OP_LOAD_TWOWORD(o,index,type) \
+ do { \
+ INDEX_TWOWORD(index); \
+ OP_LOCALINDEX(o,index); \
+ LOCALTYPE_USED(index,type); \
+ } while (0)
+
+# define OP_STORE_ONEWORD(o,index,type) \
+ do { \
+ INDEX_ONEWORD(index); \
+ OP_PREPARE_ZEROFLAGS(o); \
+ iptr->dst.varindex = (index); \
+ LOCALTYPE_USED(index,type); \
+ PINC; \
+ } while (0)
+
+# define OP_STORE_TWOWORD(o,index,type) \
+ do { \
+ INDEX_TWOWORD(index); \
+ OP_PREPARE_ZEROFLAGS(o); \
+ iptr->dst.varindex = (index); \
+ LOCALTYPE_USED(index,type); \
+ PINC; \
+ } while (0)
+
+#define OP_BUILTIN_CHECK_EXCEPTION(bte) \
+ code_unflag_leafmethod(code); \
+ OP_PREPARE_FLAGS(ICMD_BUILTIN, INS_FLAG_CHECK); \
+ iptr->sx.s23.s3.bte = (bte); \
+ PINC
+
+#define OP_BUILTIN_NO_EXCEPTION(bte) \
+ code_unflag_leafmethod(code); \
+ OP_PREPARE_ZEROFLAGS(ICMD_BUILTIN); \
+ iptr->sx.s23.s3.bte = (bte); \
+ PINC
+
+#define OP_BUILTIN_ARITHMETIC(opcode, bte) \
+ code_unflag_leafmethod(code); \
+ OP_PREPARE_FLAGS(opcode, INS_FLAG_CHECK); \
+ iptr->sx.s23.s3.bte = (bte); \
+ PINC
+
+/* CAUTION: You must set iptr->flags yourself when using this! */
+#define OP_FMIREF_PREPARE(o, fmiref) \
+ OP_PREPARE(o); \
+ iptr->sx.s23.s3.fmiref = (fmiref);
+
+
+/* function prototypes ********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool parse(jitdata *jd);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PARSE_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
#include "vm/jit/methodheader.h"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher-common.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "vm/jit/powerpc/darwin/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
#include "vm/jit/powerpc/linux/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
#include "vm/jit/powerpc/netbsd/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
#include "vm/jit/emit-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher-common.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "vm/jit/powerpc64/linux/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
#include "arch.h"
#include "vm/jit/jit.hpp"
-#include "vm/jit/verify/typeinfo.h"
+#include "vm/jit/verify/typeinfo.hpp"
/************************* pseudo variable structure **************************/
#include "toolbox/logging.h"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/globals.hpp"
#include "vm/options.h"
#include "vm/string.hpp"
#include "arch.h"
#include "md-abi.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/stacktrace.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
#include "vm/jit/methodheader.h"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher-common.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "config.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/types.h"
#include "vm/jit/show.hpp"
#include "vm/jit/disass.h"
#include "vm/jit/stack.h"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#if defined(ENABLE_DEBUG_FILTER)
# include <sys/types.h>
#include "vm/jit/emit-common.hpp"
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher.h"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "vm/jit/sparc64/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
#include "vm/jit/abi.h"
#include "vm/jit/cfg.h"
#include "vm/jit/codegen-common.hpp"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/show.hpp"
#if defined(ENABLE_DISASSEMBLER)
#include "vm/globals.hpp"
#include "vm/javaobjects.hpp"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/string.hpp"
#include "vm/vm.hpp"
#include "mm/dumpmemory.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/jit/abi.h"
#include <stdint.h>
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/jit/code.hpp"
#include <stdint.h>
-#include "vm/method.h"
+#include "vm/method.hpp"
#if !defined(NDEBUG)
noinst_LTLIBRARIES = libverify.la
libverify_la_SOURCES = \
- typecheck.c \
- typecheck.h \
- typecheck-common.c \
- typecheck-common.h \
+ typecheck.cpp \
+ typecheck.hpp \
+ typecheck-common.cpp \
+ typecheck-common.hpp \
typecheck-builtins.inc \
typecheck-fields.inc \
typecheck-invoke.inc \
typecheck-multianewarray.inc \
- typecheck-stackbased.c \
- typecheck-typeinferer.c \
- typecheck-typeinferer.h \
+ typecheck-stackbased.cpp \
+ typecheck-typeinferer.cpp \
+ typecheck-typeinferer.hpp \
typecheck-stackbased-gen.inc \
typecheck-variablesbased-gen.inc \
typecheck-typeinferer-gen.inc \
- typeinfo.c \
- typeinfo.h
+ typeinfo.cpp \
+ typeinfo.hpp
## Local variables:
+++ /dev/null
-/* src/vm/jit/verify/icmds.c - ICMD-specific type checking code
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-#if 0 /* (needed for code examples in the following comment) */
-/******************************************************************************/
-/* This file contains ICMD-specific code for type checking and type
- * inference. It is an input file for the verifier generator
- * (src/vm/jit/verify/generate.pl). The verifier generator creates
- * code for three compiler passes:
- * - stack-based type-infering verification
- * - vasiables-based type-infering verification
- * - type inference pass (no verification - used for optimizing compiler)
- *
- * The rest of this file must consist of "case" clauses starting in
- * the first column. Each clause can be marked with tags like this:
- *
- */ case ICMD_CONSTANT: /* {TAG, TAG, ...} */
-/*
- * This must be on one line. The following tags are defined:
- * STACKBASED..........use this clause for the stack-based verifier
- * VARIABLESBASED......use this clause for the variables-based verifier
- * TYPEINFERER.........use this clause for the type inference pass
- * ALL.................use for all passes
- *
- * If no tag is specified, {STACKBASED,VARIABLESBASED} is assumed.
- *
- * There are also tags that can be used inside a clause like this:
- *
- */ /* {TAG} */
-/*
- * The following tags are defined within clauses:
- * RESULTNOW...........generate code for modelling the stack action
- * _before_ the user-defined code in the clause
- * (Default is to model the stack action afterwards.)
- *
- * The following macros are pre-defined:
- *
- * TYPECHECK_STACKBASED.......iff compiling the stack-based verifier
- * TYPECHECK_VARIABLESBASED...iff compiling the variables-based verifier
- * TYPECHECK_TYPEINFERER......iff compiling the type inference pass
- *
-/******************************************************************************/
-#endif /* (end #if 0) */
-
-
-/* this marker is needed by generate.pl: */
-/* {START_OF_CODE} */
-
- /****************************************/
- /* MOVE/COPY */
-
- /* We just need to copy the typeinfo */
- /* for slots containing addresses. */
-
- /* (These are only used by the variables based verifier.) */
-
-case ICMD_MOVE: /* {VARIABLESBASED,TYPEINFERER} */
-case ICMD_COPY: /* {VARIABLESBASED,TYPEINFERER} */
- TYPECHECK_COUNT(stat_ins_stack);
- COPYTYPE(IPTR->s1, IPTR->dst);
- DST->type = OP1->type;
- break;
-
- /****************************************/
- /* LOADING ADDRESS FROM VARIABLE */
-
-case ICMD_ALOAD: /* {ALL} */
- TYPECHECK_COUNT(stat_ins_aload);
-
-#if !defined(TYPECHECK_TYPEINFERER)
- /* loading a returnAddress is not allowed */
- if (!TYPEDESC_IS_REFERENCE(*OP1)) {
- VERIFY_ERROR("illegal instruction: ALOAD loading non-reference");
- }
-#endif
- TYPEINFO_COPY(OP1->typeinfo,DST->typeinfo);
- break;
-
- /****************************************/
- /* STORING ADDRESS TO VARIABLE */
-
-case ICMD_ASTORE: /* {ALL} */
- TYPEINFO_COPY(OP1->typeinfo, DST->typeinfo);
- break;
-
- /****************************************/
- /* LOADING ADDRESS FROM ARRAY */
-
-case ICMD_AALOAD: /* {ALL} */
-#if !defined(TYPECHECK_TYPEINFERER)
- if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(OP1->typeinfo))
- VERIFY_ERROR("illegal instruction: AALOAD on non-reference array");
-#endif
-
- if (!typeinfo_init_component(&OP1->typeinfo,&DST->typeinfo))
- EXCEPTION;
- break;
-
- /****************************************/
- /* FIELD ACCESS */
-
-case ICMD_PUTFIELD: /* {STACKBASED} */
- CHECK_STACK_DEPTH(2);
- if (!IS_CAT1(stack[0])) {
- CHECK_STACK_DEPTH(3);
- stack -= 1;
- }
- CHECK_STACK_TYPE(stack[-1], TYPE_ADR);
- stack = typecheck_stackbased_verify_fieldaccess(STATE, stack-1, stack, stack-2);
- if (stack == NULL)
- EXCEPTION;
- break;
-
-case ICMD_PUTSTATIC: /* {STACKBASED} */
- CHECK_STACK_DEPTH(1);
- if (!IS_CAT1(stack[0])) {
- /* (stack depth >= 2 is guaranteed) */
- stack -= 1;
- }
- stack = typecheck_stackbased_verify_fieldaccess(STATE, NULL, stack, stack-1);
- if (stack == NULL)
- EXCEPTION;
- break;
-
-case ICMD_GETFIELD: /* {STACKBASED} */
- CHECK_STACK_TYPE(stack[0], TYPE_ADR);
- stack = typecheck_stackbased_verify_fieldaccess(STATE, stack, NULL, stack-1);
- if (stack == NULL)
- EXCEPTION;
- break;
-
-case ICMD_GETSTATIC: /* {STACKBASED} */
- stack = typecheck_stackbased_verify_fieldaccess(STATE, NULL, NULL, stack);
- if (stack == NULL)
- EXCEPTION;
- break;
-
-case ICMD_PUTFIELD: /* {VARIABLESBASED} */
- if (!handle_fieldaccess(state, VAROP(iptr->s1), VAROP(iptr->sx.s23.s2)))
- return false;
- maythrow = true;
- break;
-
-case ICMD_PUTSTATIC: /* {VARIABLESBASED} */
- if (!handle_fieldaccess(state, NULL, VAROP(iptr->s1)))
- return false;
- maythrow = true;
- break;
-
-case ICMD_PUTFIELDCONST: /* {VARIABLESBASED} */
- /* XXX this mess will go away with const operands */
- INSTRUCTION_GET_FIELDREF(state->iptr, fieldref);
- constvalue.type = fieldref->parseddesc.fd->type;
- if (IS_ADR_TYPE(constvalue.type)) {
- if (state->iptr->sx.val.anyptr) {
- classinfo *cc = (state->iptr->flags.bits & INS_FLAG_CLASS)
- ? class_java_lang_Class : class_java_lang_String;
- assert(cc);
- assert(cc->state & CLASS_LINKED);
- typeinfo_init_classinfo(&(constvalue.typeinfo), cc);
- }
- else {
- TYPEINFO_INIT_NULLTYPE(constvalue.typeinfo);
- }
- }
- if (!handle_fieldaccess(state, VAROP(iptr->s1), &constvalue))
- return false;
- maythrow = true;
- break;
-
-case ICMD_PUTSTATICCONST: /* {VARIABLESBASED} */
- /* XXX this mess will go away with const operands */
- INSTRUCTION_GET_FIELDREF(state->iptr, fieldref);
- constvalue.type = fieldref->parseddesc.fd->type;
- if (IS_ADR_TYPE(constvalue.type)) {
- if (state->iptr->sx.val.anyptr) {
- classinfo *cc = (state->iptr->flags.bits & INS_FLAG_CLASS)
- ? class_java_lang_Class : class_java_lang_String;
- assert(cc);
- assert(cc->state & CLASS_LINKED);
- typeinfo_init_classinfo(&(constvalue.typeinfo), cc);
- }
- else {
- TYPEINFO_INIT_NULLTYPE(constvalue.typeinfo);
- }
- }
- if (!handle_fieldaccess(state, NULL, &constvalue))
- return false;
- maythrow = true;
- break;
-
-case ICMD_GETFIELD: /* {VARIABLESBASED,TYPEINFERER} */
- if (!handle_fieldaccess(state, VAROP(iptr->s1), NULL))
- return false;
- maythrow = true;
- break;
-
-case ICMD_GETSTATIC: /* {VARIABLESBASED,TYPEINFERER} */
- if (!handle_fieldaccess(state, NULL, NULL))
- return false;
- maythrow = true;
- break;
-
- /****************************************/
- /* PRIMITIVE ARRAY ACCESS */
-
-case ICMD_ARRAYLENGTH:
- if (!TYPEINFO_MAYBE_ARRAY(OP1->typeinfo)
- && OP1->typeinfo.typeclass.cls != pseudo_class_Arraystub)
- VERIFY_ERROR("illegal instruction: ARRAYLENGTH on non-array");
- break;
-
-case ICMD_BALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BOOLEAN)
- && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BYTE))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_CALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_CHAR))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_DALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_DOUBLE))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_FALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_FLOAT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_IALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_INT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_SALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_SHORT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_LALOAD:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_LONG))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_BASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BOOLEAN)
- && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BYTE))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_CASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_CHAR))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_DASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_DOUBLE))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_FASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_FLOAT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_IASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_INT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_SASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_SHORT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_LASTORE:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_LONG))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_AASTORE:
- /* we just check the basic input types and that the */
- /* destination is an array of references. Assignability to */
- /* the actual array must be checked at runtime, each time the */
- /* instruction is performed. (See builtin_canstore.) */
- if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(OP1->typeinfo))
- VERIFY_ERROR("illegal instruction: AASTORE to non-reference array");
- break;
-
-case ICMD_IASTORECONST:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_INT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_LASTORECONST:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_LONG))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_BASTORECONST:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_BOOLEAN)
- && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_BYTE))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_CASTORECONST:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_CHAR))
- VERIFY_ERROR("Array type mismatch");
- break;
-
-case ICMD_SASTORECONST:
- if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_SHORT))
- VERIFY_ERROR("Array type mismatch");
- break;
-
- /****************************************/
- /* ADDRESS CONSTANTS */
-
-case ICMD_ACONST: /* {ALL} */
- if (IPTR->flags.bits & INS_FLAG_CLASS) {
- /* a java.lang.Class reference */
- TYPEINFO_INIT_JAVA_LANG_CLASS(DST->typeinfo,IPTR->sx.val.c);
- }
- else {
- if (IPTR->sx.val.anyptr == NULL)
- TYPEINFO_INIT_NULLTYPE(DST->typeinfo);
- else {
- /* string constant (or constant for builtin function) */
- typeinfo_init_classinfo(&(DST->typeinfo),class_java_lang_String);
- }
- }
- break;
-
- /****************************************/
- /* CHECKCAST AND INSTANCEOF */
-
-case ICMD_CHECKCAST: /* {ALL} */
-#if !defined(TYPECHECK_TYPEINFERER)
- /* returnAddress is not allowed */
- if (!TYPEINFO_IS_REFERENCE(OP1->typeinfo))
- VERIFY_ERROR("Illegal instruction: CHECKCAST on non-reference");
-#endif
-
- /* XXX only if narrower */
- if (!typeinfo_init_class(&(DST->typeinfo),IPTR->sx.s23.s3.c))
- EXCEPTION;
- break;
-
-case ICMD_INSTANCEOF:
- /* returnAddress is not allowed */
- if (!TYPEINFO_IS_REFERENCE(OP1->typeinfo))
- VERIFY_ERROR("Illegal instruction: INSTANCEOF on non-reference");
-
- /* XXX should propagate type information to the following if-branches */
- break;
-
- /****************************************/
- /* BRANCH INSTRUCTIONS */
-
-case ICMD_GOTO: /* {ALL} */
-case ICMD_IFNULL: /* {ALL} */
-case ICMD_IFNONNULL: /* {ALL} */
-case ICMD_IFEQ: /* {ALL} */
-case ICMD_IFNE: /* {ALL} */
-case ICMD_IFLT: /* {ALL} */
-case ICMD_IFGE: /* {ALL} */
-case ICMD_IFGT: /* {ALL} */
-case ICMD_IFLE: /* {ALL} */
-case ICMD_IF_ICMPEQ: /* {ALL} */
-case ICMD_IF_ICMPNE: /* {ALL} */
-case ICMD_IF_ICMPLT: /* {ALL} */
-case ICMD_IF_ICMPGE: /* {ALL} */
-case ICMD_IF_ICMPGT: /* {ALL} */
-case ICMD_IF_ICMPLE: /* {ALL} */
-case ICMD_IF_ACMPEQ: /* {ALL} */
-case ICMD_IF_ACMPNE: /* {ALL} */
-
-case ICMD_IF_LEQ: /* {ALL} */
-case ICMD_IF_LNE: /* {ALL} */
-case ICMD_IF_LLT: /* {ALL} */
-case ICMD_IF_LGE: /* {ALL} */
-case ICMD_IF_LGT: /* {ALL} */
-case ICMD_IF_LLE: /* {ALL} */
-
-case ICMD_IF_LCMPEQ: /* {ALL} */
-case ICMD_IF_LCMPNE: /* {ALL} */
-case ICMD_IF_LCMPLT: /* {ALL} */
-case ICMD_IF_LCMPGE: /* {ALL} */
-case ICMD_IF_LCMPGT: /* {ALL} */
-case ICMD_IF_LCMPLE: /* {ALL} */
- /* {RESULTNOW} */
- TYPECHECK_COUNT(stat_ins_branch);
-
- /* propagate stack and variables to the target block */
- REACH(IPTR->dst);
- break;
-
- /****************************************/
- /* SWITCHES */
-
-case ICMD_TABLESWITCH: /* {ALL} */
- /* {RESULTNOW} */
- TYPECHECK_COUNT(stat_ins_switch);
-
- table = IPTR->dst.table;
- i = IPTR->sx.s23.s3.tablehigh
- - IPTR->sx.s23.s2.tablelow + 1 + 1; /* plus default */
-
- while (--i >= 0) {
- REACH(*table);
- table++;
- }
-
- LOG("switch done");
- break;
-
-case ICMD_LOOKUPSWITCH: /* {ALL} */
- /* {RESULTNOW} */
- TYPECHECK_COUNT(stat_ins_switch);
-
- lookup = IPTR->dst.lookup;
- i = IPTR->sx.s23.s2.lookupcount;
- REACH(IPTR->sx.s23.s3.lookupdefault);
-
- while (--i >= 0) {
- REACH(lookup->target);
- lookup++;
- }
-
- LOG("switch done");
- break;
-
-
- /****************************************/
- /* ADDRESS RETURNS AND THROW */
-
-case ICMD_ATHROW:
- TYPECHECK_COUNT(stat_ins_athrow);
- r = typeinfo_is_assignable_to_class(&OP1->typeinfo,
- CLASSREF_OR_CLASSINFO(class_java_lang_Throwable));
- if (r == typecheck_FALSE)
- VERIFY_ERROR("illegal instruction: ATHROW on non-Throwable");
- if (r == typecheck_FAIL)
- EXCEPTION;
- if (r == typecheck_MAYBE) {
- /* the check has to be postponed. we need a patcher */
- TYPECHECK_COUNT(stat_ins_athrow_unresolved);
- IPTR->sx.s23.s2.uc = create_unresolved_class(
- METHOD,
- /* XXX make this more efficient, use class_java_lang_Throwable
- * directly */
- class_get_classref(METHOD->clazz,utf_java_lang_Throwable),
- &OP1->typeinfo);
- IPTR->flags.bits |= INS_FLAG_UNRESOLVED;
- }
- break;
-
-case ICMD_ARETURN:
- TYPECHECK_COUNT(stat_ins_areturn);
- if (!TYPEINFO_IS_REFERENCE(OP1->typeinfo))
- VERIFY_ERROR("illegal instruction: ARETURN on non-reference");
-
- if (STATE->returntype.type != TYPE_ADR
- || (r = typeinfo_is_assignable(&OP1->typeinfo,&(STATE->returntype.typeinfo)))
- == typecheck_FALSE)
- VERIFY_ERROR("Return type mismatch");
- if (r == typecheck_FAIL)
- EXCEPTION;
- if (r == typecheck_MAYBE) {
- /* the check has to be postponed, we need a patcher */
- TYPECHECK_COUNT(stat_ins_areturn_unresolved);
- IPTR->sx.s23.s2.uc = create_unresolved_class(
- METHOD,
- METHOD->parseddesc->returntype.classref,
- &OP1->typeinfo);
- IPTR->flags.bits |= INS_FLAG_UNRESOLVED;
- }
- goto return_tail;
-
- /****************************************/
- /* PRIMITIVE RETURNS */
-
-case ICMD_IRETURN:
- if (STATE->returntype.type != TYPE_INT)
- VERIFY_ERROR("Return type mismatch");
- goto return_tail;
-
-case ICMD_LRETURN:
- if (STATE->returntype.type != TYPE_LNG)
- VERIFY_ERROR("Return type mismatch");
- goto return_tail;
-
-case ICMD_FRETURN:
- if (STATE->returntype.type != TYPE_FLT)
- VERIFY_ERROR("Return type mismatch");
- goto return_tail;
-
-case ICMD_DRETURN:
- if (STATE->returntype.type != TYPE_DBL)
- VERIFY_ERROR("Return type mismatch");
- goto return_tail;
-
-case ICMD_RETURN:
- if (STATE->returntype.type != TYPE_VOID)
- VERIFY_ERROR("Return type mismatch");
-
-return_tail:
- TYPECHECK_COUNT(stat_ins_primitive_return);
-
- if (STATE->initmethod && METHOD->clazz != class_java_lang_Object) {
- /* Check if the 'this' instance has been initialized. */
- LOG("Checking <init> marker");
-#if defined(TYPECHECK_VARIABLESBASED)
- if (!typevector_checktype(jd->var,STATE->numlocals-1,TYPE_INT))
-#else
- if (STATE->locals[STATE->numlocals-1].type != TYPE_INT)
-#endif
- VERIFY_ERROR("<init> method does not initialize 'this'");
- }
- break;
-
- /****************************************/
- /* SUBROUTINE INSTRUCTIONS */
-
-case ICMD_JSR: /* {VARIABLESBASED,TYPEINFERER} */
- TYPEINFO_INIT_RETURNADDRESS(DST->typeinfo, BPTR->next);
- REACH(IPTR->sx.s23.s3.jsrtarget);
- break;
-
-case ICMD_JSR: /* {STACKBASED} */
- /* {RESULTNOW} */
- tbptr = IPTR->sx.s23.s3.jsrtarget.block;
-
- TYPEINFO_INIT_RETURNADDRESS(stack[0].typeinfo, tbptr);
- REACH_BLOCK(tbptr);
-
- stack = typecheck_stackbased_jsr(STATE, stack, stackfloor);
- if (stack == NULL)
- EXCEPTION;
- break;
-
-case ICMD_RET: /* {VARIABLESBASED,TYPEINFERER} */
-#if !defined(TYPECHECK_TYPEINFERER)
- /* check returnAddress variable */
- if (!typevector_checkretaddr(jd->var,IPTR->s1.varindex))
- VERIFY_ERROR("illegal instruction: RET using non-returnAddress variable");
-#endif
- REACH(IPTR->dst);
- break;
-
-case ICMD_RET: /* {STACKBASED} */
- /* {RESULTNOW} */
- CHECK_LOCAL_TYPE(IPTR->s1.varindex, TYPE_RET);
- if (!TYPEINFO_IS_PRIMITIVE(STATE->locals[IPTR->s1.varindex].typeinfo))
- VERIFY_ERROR("illegal instruction: RET using non-returnAddress variable");
-
- if (!typecheck_stackbased_ret(STATE, stack, stackfloor))
- EXCEPTION;
- break;
-
- /****************************************/
- /* INVOKATIONS */
-
-case ICMD_INVOKEVIRTUAL: /* {VARIABLESBASED,TYPEINFERER} */
-case ICMD_INVOKESPECIAL: /* {VARIABLESBASED,TYPEINFERER} */
-case ICMD_INVOKESTATIC: /* {VARIABLESBASED,TYPEINFERER} */
-case ICMD_INVOKEINTERFACE: /* {VARIABLESBASED,TYPEINFERER} */
- TYPECHECK_COUNT(stat_ins_invoke);
- if (!handle_invocation(state))
- EXCEPTION;
- TYPECHECK_COUNTIF(INSTRUCTION_IS_UNRESOLVED(IPTR), stat_ins_invoke_unresolved);
- break;
-
-case ICMD_INVOKEVIRTUAL: /* {STACKBASED} */
-case ICMD_INVOKESPECIAL: /* {STACKBASED} */
-case ICMD_INVOKESTATIC: /* {STACKBASED} */
-case ICMD_INVOKEINTERFACE: /* {STACKBASED} */
- TYPECHECK_COUNT(stat_ins_invoke);
-
- INSTRUCTION_GET_METHODDESC(IPTR, md);
- CHECK_STACK_DEPTH(md->paramslots);
-
- if (!typecheck_stackbased_verify_invocation(STATE, stack, stackfloor))
- EXCEPTION;
-
- stack -= md->paramslots;
-
- if (md->returntype.type != TYPE_VOID) {
- if (IS_2_WORD_TYPE(md->returntype.type)) {
- CHECK_STACK_SPACE(2);
- stack += 2;
- stack[0].type = TYPE_VOID;
- stack[-1].type = md->returntype.type;
- }
- else {
- CHECK_STACK_SPACE(1);
- stack += 1;
- stack[0].type = md->returntype.type;
- }
- }
- TYPECHECK_COUNTIF(INSTRUCTION_IS_UNRESOLVED(IPTR), stat_ins_invoke_unresolved);
- break;
-
- /****************************************/
- /* MULTIANEWARRAY */
-
-case ICMD_MULTIANEWARRAY: /* {VARIABLESBASED,TYPEINFERER} */
- if (!handle_multianewarray(STATE))
- EXCEPTION;
- break;
-
-case ICMD_MULTIANEWARRAY: /* {STACKBASED} */
- if (!typecheck_stackbased_multianewarray(STATE, stack, stackfloor))
- EXCEPTION;
- stack -= (IPTR->s1.argcount - 1);
- stack[0].type = TYPE_ADR;
- break;
-
- /****************************************/
- /* BUILTINS */
-
-case ICMD_BUILTIN: /* {VARIABLESBASED,TYPEINFERER} */
- TYPECHECK_COUNT(stat_ins_builtin);
- if (!handle_builtin(state))
- EXCEPTION;
- break;
-
-case ICMD_BUILTIN: /* {STACKBASED} */
- TYPECHECK_COUNT(stat_ins_builtin);
- if (!typecheck_stackbased_verify_builtin(STATE, stack, stackfloor))
- EXCEPTION;
-
- /* pop operands and push return value */
- {
- u1 rtype = IPTR->sx.s23.s3.bte->md->returntype.type;
- stack -= IPTR->sx.s23.s3.bte->md->paramslots;
- if (rtype != TYPE_VOID) {
- if (IS_2_WORD_TYPE(rtype))
- stack += 2;
- else
- stack += 1;
- }
- }
- break;
-
-/* the following code is only used by the stackbased verifier */
-
-case ICMD_POP: /* {STACKBASED} */
- /* we pop 1 */
- CHECK_CAT1(stack[0]);
- break;
-
-case ICMD_POP2: /* {STACKBASED} */
- /* we pop either 11 or 2 */
- if (IS_CAT1(stack[0]))
- CHECK_CAT1(stack[-1]);
- break;
-
-case ICMD_SWAP: /* {STACKBASED} */
- CHECK_CAT1(stack[0]);
- CHECK_CAT1(stack[-1]);
-
- COPY_SLOT(stack[ 0], temp );
- COPY_SLOT(stack[-1], stack[ 0]);
- COPY_SLOT(temp , stack[-1]);
- break;
-
-case ICMD_DUP: /* {STACKBASED} */
- /* we dup 1 */
- CHECK_CAT1(stack[0]);
-
- COPY_SLOT(stack[ 0], stack[ 1]);
- break;
-
-case ICMD_DUP_X1: /* {STACKBASED} */
- /* we dup 1 */
- CHECK_CAT1(stack[0]);
- /* we skip 1 */
- CHECK_CAT1(stack[-1]);
-
- COPY_SLOT(stack[ 0], stack[ 1]);
- COPY_SLOT(stack[-1], stack[ 0]);
- COPY_SLOT(stack[ 1], stack[-1]);
- break;
-
-case ICMD_DUP_X2: /* {STACKBASED} */
- /* we dup 1 */
- CHECK_CAT1(stack[0]);
- /* we skip either 11 or 2 */
- if (IS_CAT1(stack[-1]))
- CHECK_CAT1(stack[-2]);
-
- COPY_SLOT(stack[ 0], stack[ 1]);
- COPY_SLOT(stack[-1], stack[ 0]);
- COPY_SLOT(stack[-2], stack[-1]);
- COPY_SLOT(stack[ 1], stack[-2]);
- break;
-
-case ICMD_DUP2: /* {STACKBASED} */
- /* we dup either 11 or 2 */
- if (IS_CAT1(stack[0]))
- CHECK_CAT1(stack[-1]);
-
- COPY_SLOT(stack[ 0], stack[ 2]);
- COPY_SLOT(stack[-1], stack[ 1]);
- break;
-
-case ICMD_DUP2_X1: /* {STACKBASED} */
- /* we dup either 11 or 2 */
- if (IS_CAT1(stack[0]))
- CHECK_CAT1(stack[-1]);
- /* we skip 1 */
- CHECK_CAT1(stack[-2]);
-
- COPY_SLOT(stack[ 0], stack[ 2]);
- COPY_SLOT(stack[-1], stack[ 1]);
- COPY_SLOT(stack[-2], stack[ 0]);
- COPY_SLOT(stack[ 2], stack[-1]);
- COPY_SLOT(stack[ 1], stack[-2]);
- break;
-
-case ICMD_DUP2_X2: /* {STACKBASED} */
- /* we dup either 11 or 2 */
- if (IS_CAT1(stack[0]))
- CHECK_CAT1(stack[-1]);
- /* we skip either 11 or 2 */
- if (IS_CAT1(stack[-2]))
- CHECK_CAT1(stack[-3]);
-
- COPY_SLOT(stack[ 0], stack[ 2]);
- COPY_SLOT(stack[-1], stack[ 1]);
- COPY_SLOT(stack[-2], stack[ 0]);
- COPY_SLOT(stack[-3], stack[-1]);
- COPY_SLOT(stack[ 2], stack[-2]);
- COPY_SLOT(stack[ 1], stack[-3]);
- break;
-
-
-/* this marker is needed by generate.pl: */
-/* {END_OF_CODE} */
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/verify/icmds.c - ICMD-specific type checking code
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+#if 0 /* (needed for code examples in the following comment) */
+/******************************************************************************/
+/* This file contains ICMD-specific code for type checking and type
+ * inference. It is an input file for the verifier generator
+ * (src/vm/jit/verify/generate.pl). The verifier generator creates
+ * code for three compiler passes:
+ * - stack-based type-infering verification
+ * - vasiables-based type-infering verification
+ * - type inference pass (no verification - used for optimizing compiler)
+ *
+ * The rest of this file must consist of "case" clauses starting in
+ * the first column. Each clause can be marked with tags like this:
+ *
+ */ case ICMD_CONSTANT: /* {TAG, TAG, ...} */
+/*
+ * This must be on one line. The following tags are defined:
+ * STACKBASED..........use this clause for the stack-based verifier
+ * VARIABLESBASED......use this clause for the variables-based verifier
+ * TYPEINFERER.........use this clause for the type inference pass
+ * ALL.................use for all passes
+ *
+ * If no tag is specified, {STACKBASED,VARIABLESBASED} is assumed.
+ *
+ * There are also tags that can be used inside a clause like this:
+ *
+ */ /* {TAG} */
+/*
+ * The following tags are defined within clauses:
+ * RESULTNOW...........generate code for modelling the stack action
+ * _before_ the user-defined code in the clause
+ * (Default is to model the stack action afterwards.)
+ *
+ * The following macros are pre-defined:
+ *
+ * TYPECHECK_STACKBASED.......iff compiling the stack-based verifier
+ * TYPECHECK_VARIABLESBASED...iff compiling the variables-based verifier
+ * TYPECHECK_TYPEINFERER......iff compiling the type inference pass
+ *
+/******************************************************************************/
+#endif /* (end #if 0) */
+
+
+/* this marker is needed by generate.pl: */
+/* {START_OF_CODE} */
+
+ /****************************************/
+ /* MOVE/COPY */
+
+ /* We just need to copy the typeinfo */
+ /* for slots containing addresses. */
+
+ /* (These are only used by the variables based verifier.) */
+
+case ICMD_MOVE: /* {VARIABLESBASED,TYPEINFERER} */
+case ICMD_COPY: /* {VARIABLESBASED,TYPEINFERER} */
+ TYPECHECK_COUNT(stat_ins_stack);
+ COPYTYPE(IPTR->s1, IPTR->dst);
+ DST->type = OP1->type;
+ break;
+
+ /****************************************/
+ /* LOADING ADDRESS FROM VARIABLE */
+
+case ICMD_ALOAD: /* {ALL} */
+ TYPECHECK_COUNT(stat_ins_aload);
+
+#if !defined(TYPECHECK_TYPEINFERER)
+ /* loading a returnAddress is not allowed */
+ if (!TYPEDESC_IS_REFERENCE(*OP1)) {
+ VERIFY_ERROR("illegal instruction: ALOAD loading non-reference");
+ }
+#endif
+ TYPEINFO_COPY(OP1->typeinfo,DST->typeinfo);
+ break;
+
+ /****************************************/
+ /* STORING ADDRESS TO VARIABLE */
+
+case ICMD_ASTORE: /* {ALL} */
+ TYPEINFO_COPY(OP1->typeinfo, DST->typeinfo);
+ break;
+
+ /****************************************/
+ /* LOADING ADDRESS FROM ARRAY */
+
+case ICMD_AALOAD: /* {ALL} */
+#if !defined(TYPECHECK_TYPEINFERER)
+ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(OP1->typeinfo))
+ VERIFY_ERROR("illegal instruction: AALOAD on non-reference array");
+#endif
+
+ if (!typeinfo_init_component(&OP1->typeinfo,&DST->typeinfo))
+ EXCEPTION;
+ break;
+
+ /****************************************/
+ /* FIELD ACCESS */
+
+case ICMD_PUTFIELD: /* {STACKBASED} */
+ CHECK_STACK_DEPTH(2);
+ if (!IS_CAT1(stack[0])) {
+ CHECK_STACK_DEPTH(3);
+ stack -= 1;
+ }
+ CHECK_STACK_TYPE(stack[-1], TYPE_ADR);
+ stack = typecheck_stackbased_verify_fieldaccess(STATE, stack-1, stack, stack-2);
+ if (stack == NULL)
+ EXCEPTION;
+ break;
+
+case ICMD_PUTSTATIC: /* {STACKBASED} */
+ CHECK_STACK_DEPTH(1);
+ if (!IS_CAT1(stack[0])) {
+ /* (stack depth >= 2 is guaranteed) */
+ stack -= 1;
+ }
+ stack = typecheck_stackbased_verify_fieldaccess(STATE, NULL, stack, stack-1);
+ if (stack == NULL)
+ EXCEPTION;
+ break;
+
+case ICMD_GETFIELD: /* {STACKBASED} */
+ CHECK_STACK_TYPE(stack[0], TYPE_ADR);
+ stack = typecheck_stackbased_verify_fieldaccess(STATE, stack, NULL, stack-1);
+ if (stack == NULL)
+ EXCEPTION;
+ break;
+
+case ICMD_GETSTATIC: /* {STACKBASED} */
+ stack = typecheck_stackbased_verify_fieldaccess(STATE, NULL, NULL, stack);
+ if (stack == NULL)
+ EXCEPTION;
+ break;
+
+case ICMD_PUTFIELD: /* {VARIABLESBASED} */
+ if (!handle_fieldaccess(state, VAROP(iptr->s1), VAROP(iptr->sx.s23.s2)))
+ return false;
+ maythrow = true;
+ break;
+
+case ICMD_PUTSTATIC: /* {VARIABLESBASED} */
+ if (!handle_fieldaccess(state, NULL, VAROP(iptr->s1)))
+ return false;
+ maythrow = true;
+ break;
+
+case ICMD_PUTFIELDCONST: /* {VARIABLESBASED} */
+ /* XXX this mess will go away with const operands */
+ INSTRUCTION_GET_FIELDREF(state->iptr, fieldref);
+ constvalue.type = fieldref->parseddesc.fd->type;
+ if (IS_ADR_TYPE(constvalue.type)) {
+ if (state->iptr->sx.val.anyptr) {
+ classinfo *cc = (state->iptr->flags.bits & INS_FLAG_CLASS)
+ ? class_java_lang_Class : class_java_lang_String;
+ assert(cc);
+ assert(cc->state & CLASS_LINKED);
+ typeinfo_init_classinfo(&(constvalue.typeinfo), cc);
+ }
+ else {
+ TYPEINFO_INIT_NULLTYPE(constvalue.typeinfo);
+ }
+ }
+ if (!handle_fieldaccess(state, VAROP(iptr->s1), &constvalue))
+ return false;
+ maythrow = true;
+ break;
+
+case ICMD_PUTSTATICCONST: /* {VARIABLESBASED} */
+ /* XXX this mess will go away with const operands */
+ INSTRUCTION_GET_FIELDREF(state->iptr, fieldref);
+ constvalue.type = fieldref->parseddesc.fd->type;
+ if (IS_ADR_TYPE(constvalue.type)) {
+ if (state->iptr->sx.val.anyptr) {
+ classinfo *cc = (state->iptr->flags.bits & INS_FLAG_CLASS)
+ ? class_java_lang_Class : class_java_lang_String;
+ assert(cc);
+ assert(cc->state & CLASS_LINKED);
+ typeinfo_init_classinfo(&(constvalue.typeinfo), cc);
+ }
+ else {
+ TYPEINFO_INIT_NULLTYPE(constvalue.typeinfo);
+ }
+ }
+ if (!handle_fieldaccess(state, NULL, &constvalue))
+ return false;
+ maythrow = true;
+ break;
+
+case ICMD_GETFIELD: /* {VARIABLESBASED,TYPEINFERER} */
+ if (!handle_fieldaccess(state, VAROP(iptr->s1), NULL))
+ return false;
+ maythrow = true;
+ break;
+
+case ICMD_GETSTATIC: /* {VARIABLESBASED,TYPEINFERER} */
+ if (!handle_fieldaccess(state, NULL, NULL))
+ return false;
+ maythrow = true;
+ break;
+
+ /****************************************/
+ /* PRIMITIVE ARRAY ACCESS */
+
+case ICMD_ARRAYLENGTH:
+ if (!TYPEINFO_MAYBE_ARRAY(OP1->typeinfo)
+ && OP1->typeinfo.typeclass.cls != pseudo_class_Arraystub)
+ VERIFY_ERROR("illegal instruction: ARRAYLENGTH on non-array");
+ break;
+
+case ICMD_BALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BYTE))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_CALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_CHAR))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_DALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_DOUBLE))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_FALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_FLOAT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_IALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_INT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_SALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_SHORT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_LALOAD:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_LONG))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_BASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_BYTE))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_CASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_CHAR))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_DASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_DOUBLE))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_FASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_FLOAT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_IASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_INT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_SASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_SHORT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_LASTORE:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo,ARRAYTYPE_LONG))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_AASTORE:
+ /* we just check the basic input types and that the */
+ /* destination is an array of references. Assignability to */
+ /* the actual array must be checked at runtime, each time the */
+ /* instruction is performed. (See builtin_canstore.) */
+ if (!TYPEINFO_MAYBE_ARRAY_OF_REFS(OP1->typeinfo))
+ VERIFY_ERROR("illegal instruction: AASTORE to non-reference array");
+ break;
+
+case ICMD_IASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_INT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_LASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_LONG))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_BASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_BOOLEAN)
+ && !TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_BYTE))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_CASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_CHAR))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+case ICMD_SASTORECONST:
+ if (!TYPEINFO_MAYBE_PRIMITIVE_ARRAY(OP1->typeinfo, ARRAYTYPE_SHORT))
+ VERIFY_ERROR("Array type mismatch");
+ break;
+
+ /****************************************/
+ /* ADDRESS CONSTANTS */
+
+case ICMD_ACONST: /* {ALL} */
+ if (IPTR->flags.bits & INS_FLAG_CLASS) {
+ /* a java.lang.Class reference */
+ TYPEINFO_INIT_JAVA_LANG_CLASS(DST->typeinfo,IPTR->sx.val.c);
+ }
+ else {
+ if (IPTR->sx.val.anyptr == NULL)
+ TYPEINFO_INIT_NULLTYPE(DST->typeinfo);
+ else {
+ /* string constant (or constant for builtin function) */
+ typeinfo_init_classinfo(&(DST->typeinfo),class_java_lang_String);
+ }
+ }
+ break;
+
+ /****************************************/
+ /* CHECKCAST AND INSTANCEOF */
+
+case ICMD_CHECKCAST: /* {ALL} */
+#if !defined(TYPECHECK_TYPEINFERER)
+ /* returnAddress is not allowed */
+ if (!TYPEINFO_IS_REFERENCE(OP1->typeinfo))
+ VERIFY_ERROR("Illegal instruction: CHECKCAST on non-reference");
+#endif
+
+ /* XXX only if narrower */
+ if (!typeinfo_init_class(&(DST->typeinfo),IPTR->sx.s23.s3.c))
+ EXCEPTION;
+ break;
+
+case ICMD_INSTANCEOF:
+ /* returnAddress is not allowed */
+ if (!TYPEINFO_IS_REFERENCE(OP1->typeinfo))
+ VERIFY_ERROR("Illegal instruction: INSTANCEOF on non-reference");
+
+ /* XXX should propagate type information to the following if-branches */
+ break;
+
+ /****************************************/
+ /* BRANCH INSTRUCTIONS */
+
+case ICMD_GOTO: /* {ALL} */
+case ICMD_IFNULL: /* {ALL} */
+case ICMD_IFNONNULL: /* {ALL} */
+case ICMD_IFEQ: /* {ALL} */
+case ICMD_IFNE: /* {ALL} */
+case ICMD_IFLT: /* {ALL} */
+case ICMD_IFGE: /* {ALL} */
+case ICMD_IFGT: /* {ALL} */
+case ICMD_IFLE: /* {ALL} */
+case ICMD_IF_ICMPEQ: /* {ALL} */
+case ICMD_IF_ICMPNE: /* {ALL} */
+case ICMD_IF_ICMPLT: /* {ALL} */
+case ICMD_IF_ICMPGE: /* {ALL} */
+case ICMD_IF_ICMPGT: /* {ALL} */
+case ICMD_IF_ICMPLE: /* {ALL} */
+case ICMD_IF_ACMPEQ: /* {ALL} */
+case ICMD_IF_ACMPNE: /* {ALL} */
+
+case ICMD_IF_LEQ: /* {ALL} */
+case ICMD_IF_LNE: /* {ALL} */
+case ICMD_IF_LLT: /* {ALL} */
+case ICMD_IF_LGE: /* {ALL} */
+case ICMD_IF_LGT: /* {ALL} */
+case ICMD_IF_LLE: /* {ALL} */
+
+case ICMD_IF_LCMPEQ: /* {ALL} */
+case ICMD_IF_LCMPNE: /* {ALL} */
+case ICMD_IF_LCMPLT: /* {ALL} */
+case ICMD_IF_LCMPGE: /* {ALL} */
+case ICMD_IF_LCMPGT: /* {ALL} */
+case ICMD_IF_LCMPLE: /* {ALL} */
+ /* {RESULTNOW} */
+ TYPECHECK_COUNT(stat_ins_branch);
+
+ /* propagate stack and variables to the target block */
+ REACH(IPTR->dst);
+ break;
+
+ /****************************************/
+ /* SWITCHES */
+
+case ICMD_TABLESWITCH: /* {ALL} */
+ /* {RESULTNOW} */
+ TYPECHECK_COUNT(stat_ins_switch);
+
+ table = IPTR->dst.table;
+ i = IPTR->sx.s23.s3.tablehigh
+ - IPTR->sx.s23.s2.tablelow + 1 + 1; /* plus default */
+
+ while (--i >= 0) {
+ REACH(*table);
+ table++;
+ }
+
+ LOG("switch done");
+ break;
+
+case ICMD_LOOKUPSWITCH: /* {ALL} */
+ /* {RESULTNOW} */
+ TYPECHECK_COUNT(stat_ins_switch);
+
+ lookup = IPTR->dst.lookup;
+ i = IPTR->sx.s23.s2.lookupcount;
+ REACH(IPTR->sx.s23.s3.lookupdefault);
+
+ while (--i >= 0) {
+ REACH(lookup->target);
+ lookup++;
+ }
+
+ LOG("switch done");
+ break;
+
+
+ /****************************************/
+ /* ADDRESS RETURNS AND THROW */
+
+case ICMD_ATHROW:
+ TYPECHECK_COUNT(stat_ins_athrow);
+ r = typeinfo_is_assignable_to_class(&OP1->typeinfo,
+ CLASSREF_OR_CLASSINFO(class_java_lang_Throwable));
+ if (r == typecheck_FALSE)
+ VERIFY_ERROR("illegal instruction: ATHROW on non-Throwable");
+ if (r == typecheck_FAIL)
+ EXCEPTION;
+ if (r == typecheck_MAYBE) {
+ /* the check has to be postponed. we need a patcher */
+ TYPECHECK_COUNT(stat_ins_athrow_unresolved);
+ IPTR->sx.s23.s2.uc = create_unresolved_class(
+ METHOD,
+ /* XXX make this more efficient, use class_java_lang_Throwable
+ * directly */
+ class_get_classref(METHOD->clazz,utf_java_lang_Throwable),
+ &OP1->typeinfo);
+ IPTR->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
+ break;
+
+case ICMD_ARETURN:
+ TYPECHECK_COUNT(stat_ins_areturn);
+ if (!TYPEINFO_IS_REFERENCE(OP1->typeinfo))
+ VERIFY_ERROR("illegal instruction: ARETURN on non-reference");
+
+ if (STATE->returntype.type != TYPE_ADR
+ || (r = typeinfo_is_assignable(&OP1->typeinfo,&(STATE->returntype.typeinfo)))
+ == typecheck_FALSE)
+ VERIFY_ERROR("Return type mismatch");
+ if (r == typecheck_FAIL)
+ EXCEPTION;
+ if (r == typecheck_MAYBE) {
+ /* the check has to be postponed, we need a patcher */
+ TYPECHECK_COUNT(stat_ins_areturn_unresolved);
+ IPTR->sx.s23.s2.uc = create_unresolved_class(
+ METHOD,
+ METHOD->parseddesc->returntype.classref,
+ &OP1->typeinfo);
+ IPTR->flags.bits |= INS_FLAG_UNRESOLVED;
+ }
+ goto return_tail;
+
+ /****************************************/
+ /* PRIMITIVE RETURNS */
+
+case ICMD_IRETURN:
+ if (STATE->returntype.type != TYPE_INT)
+ VERIFY_ERROR("Return type mismatch");
+ goto return_tail;
+
+case ICMD_LRETURN:
+ if (STATE->returntype.type != TYPE_LNG)
+ VERIFY_ERROR("Return type mismatch");
+ goto return_tail;
+
+case ICMD_FRETURN:
+ if (STATE->returntype.type != TYPE_FLT)
+ VERIFY_ERROR("Return type mismatch");
+ goto return_tail;
+
+case ICMD_DRETURN:
+ if (STATE->returntype.type != TYPE_DBL)
+ VERIFY_ERROR("Return type mismatch");
+ goto return_tail;
+
+case ICMD_RETURN:
+ if (STATE->returntype.type != TYPE_VOID)
+ VERIFY_ERROR("Return type mismatch");
+
+return_tail:
+ TYPECHECK_COUNT(stat_ins_primitive_return);
+
+ if (STATE->initmethod && METHOD->clazz != class_java_lang_Object) {
+ /* Check if the 'this' instance has been initialized. */
+ LOG("Checking <init> marker");
+#if defined(TYPECHECK_VARIABLESBASED)
+ if (!typevector_checktype(jd->var,STATE->numlocals-1,TYPE_INT))
+#else
+ if (STATE->locals[STATE->numlocals-1].type != TYPE_INT)
+#endif
+ VERIFY_ERROR("<init> method does not initialize 'this'");
+ }
+ break;
+
+ /****************************************/
+ /* SUBROUTINE INSTRUCTIONS */
+
+case ICMD_JSR: /* {VARIABLESBASED,TYPEINFERER} */
+ TYPEINFO_INIT_RETURNADDRESS(DST->typeinfo, BPTR->next);
+ REACH(IPTR->sx.s23.s3.jsrtarget);
+ break;
+
+case ICMD_JSR: /* {STACKBASED} */
+ /* {RESULTNOW} */
+ tbptr = IPTR->sx.s23.s3.jsrtarget.block;
+
+ TYPEINFO_INIT_RETURNADDRESS(stack[0].typeinfo, tbptr);
+ REACH_BLOCK(tbptr);
+
+ stack = typecheck_stackbased_jsr(STATE, stack, stackfloor);
+ if (stack == NULL)
+ EXCEPTION;
+ break;
+
+case ICMD_RET: /* {VARIABLESBASED,TYPEINFERER} */
+#if !defined(TYPECHECK_TYPEINFERER)
+ /* check returnAddress variable */
+ if (!typevector_checkretaddr(jd->var,IPTR->s1.varindex))
+ VERIFY_ERROR("illegal instruction: RET using non-returnAddress variable");
+#endif
+ REACH(IPTR->dst);
+ break;
+
+case ICMD_RET: /* {STACKBASED} */
+ /* {RESULTNOW} */
+ CHECK_LOCAL_TYPE(IPTR->s1.varindex, TYPE_RET);
+ if (!TYPEINFO_IS_PRIMITIVE(STATE->locals[IPTR->s1.varindex].typeinfo))
+ VERIFY_ERROR("illegal instruction: RET using non-returnAddress variable");
+
+ if (!typecheck_stackbased_ret(STATE, stack, stackfloor))
+ EXCEPTION;
+ break;
+
+ /****************************************/
+ /* INVOKATIONS */
+
+case ICMD_INVOKEVIRTUAL: /* {VARIABLESBASED,TYPEINFERER} */
+case ICMD_INVOKESPECIAL: /* {VARIABLESBASED,TYPEINFERER} */
+case ICMD_INVOKESTATIC: /* {VARIABLESBASED,TYPEINFERER} */
+case ICMD_INVOKEINTERFACE: /* {VARIABLESBASED,TYPEINFERER} */
+ TYPECHECK_COUNT(stat_ins_invoke);
+ if (!handle_invocation(state))
+ EXCEPTION;
+ TYPECHECK_COUNTIF(INSTRUCTION_IS_UNRESOLVED(IPTR), stat_ins_invoke_unresolved);
+ break;
+
+case ICMD_INVOKEVIRTUAL: /* {STACKBASED} */
+case ICMD_INVOKESPECIAL: /* {STACKBASED} */
+case ICMD_INVOKESTATIC: /* {STACKBASED} */
+case ICMD_INVOKEINTERFACE: /* {STACKBASED} */
+ TYPECHECK_COUNT(stat_ins_invoke);
+
+ INSTRUCTION_GET_METHODDESC(IPTR, md);
+ CHECK_STACK_DEPTH(md->paramslots);
+
+ if (!typecheck_stackbased_verify_invocation(STATE, stack, stackfloor))
+ EXCEPTION;
+
+ stack -= md->paramslots;
+
+ if (md->returntype.type != TYPE_VOID) {
+ if (IS_2_WORD_TYPE(md->returntype.type)) {
+ CHECK_STACK_SPACE(2);
+ stack += 2;
+ stack[0].type = TYPE_VOID;
+ stack[-1].type = md->returntype.type;
+ }
+ else {
+ CHECK_STACK_SPACE(1);
+ stack += 1;
+ stack[0].type = md->returntype.type;
+ }
+ }
+ TYPECHECK_COUNTIF(INSTRUCTION_IS_UNRESOLVED(IPTR), stat_ins_invoke_unresolved);
+ break;
+
+ /****************************************/
+ /* MULTIANEWARRAY */
+
+case ICMD_MULTIANEWARRAY: /* {VARIABLESBASED,TYPEINFERER} */
+ if (!handle_multianewarray(STATE))
+ EXCEPTION;
+ break;
+
+case ICMD_MULTIANEWARRAY: /* {STACKBASED} */
+ if (!typecheck_stackbased_multianewarray(STATE, stack, stackfloor))
+ EXCEPTION;
+ stack -= (IPTR->s1.argcount - 1);
+ stack[0].type = TYPE_ADR;
+ break;
+
+ /****************************************/
+ /* BUILTINS */
+
+case ICMD_BUILTIN: /* {VARIABLESBASED,TYPEINFERER} */
+ TYPECHECK_COUNT(stat_ins_builtin);
+ if (!handle_builtin(state))
+ EXCEPTION;
+ break;
+
+case ICMD_BUILTIN: /* {STACKBASED} */
+ TYPECHECK_COUNT(stat_ins_builtin);
+ if (!typecheck_stackbased_verify_builtin(STATE, stack, stackfloor))
+ EXCEPTION;
+
+ /* pop operands and push return value */
+ {
+ u1 rtype = IPTR->sx.s23.s3.bte->md->returntype.type;
+ stack -= IPTR->sx.s23.s3.bte->md->paramslots;
+ if (rtype != TYPE_VOID) {
+ if (IS_2_WORD_TYPE(rtype))
+ stack += 2;
+ else
+ stack += 1;
+ }
+ }
+ break;
+
+/* the following code is only used by the stackbased verifier */
+
+case ICMD_POP: /* {STACKBASED} */
+ /* we pop 1 */
+ CHECK_CAT1(stack[0]);
+ break;
+
+case ICMD_POP2: /* {STACKBASED} */
+ /* we pop either 11 or 2 */
+ if (IS_CAT1(stack[0]))
+ CHECK_CAT1(stack[-1]);
+ break;
+
+case ICMD_SWAP: /* {STACKBASED} */
+ CHECK_CAT1(stack[0]);
+ CHECK_CAT1(stack[-1]);
+
+ COPY_SLOT(stack[ 0], temp );
+ COPY_SLOT(stack[-1], stack[ 0]);
+ COPY_SLOT(temp , stack[-1]);
+ break;
+
+case ICMD_DUP: /* {STACKBASED} */
+ /* we dup 1 */
+ CHECK_CAT1(stack[0]);
+
+ COPY_SLOT(stack[ 0], stack[ 1]);
+ break;
+
+case ICMD_DUP_X1: /* {STACKBASED} */
+ /* we dup 1 */
+ CHECK_CAT1(stack[0]);
+ /* we skip 1 */
+ CHECK_CAT1(stack[-1]);
+
+ COPY_SLOT(stack[ 0], stack[ 1]);
+ COPY_SLOT(stack[-1], stack[ 0]);
+ COPY_SLOT(stack[ 1], stack[-1]);
+ break;
+
+case ICMD_DUP_X2: /* {STACKBASED} */
+ /* we dup 1 */
+ CHECK_CAT1(stack[0]);
+ /* we skip either 11 or 2 */
+ if (IS_CAT1(stack[-1]))
+ CHECK_CAT1(stack[-2]);
+
+ COPY_SLOT(stack[ 0], stack[ 1]);
+ COPY_SLOT(stack[-1], stack[ 0]);
+ COPY_SLOT(stack[-2], stack[-1]);
+ COPY_SLOT(stack[ 1], stack[-2]);
+ break;
+
+case ICMD_DUP2: /* {STACKBASED} */
+ /* we dup either 11 or 2 */
+ if (IS_CAT1(stack[0]))
+ CHECK_CAT1(stack[-1]);
+
+ COPY_SLOT(stack[ 0], stack[ 2]);
+ COPY_SLOT(stack[-1], stack[ 1]);
+ break;
+
+case ICMD_DUP2_X1: /* {STACKBASED} */
+ /* we dup either 11 or 2 */
+ if (IS_CAT1(stack[0]))
+ CHECK_CAT1(stack[-1]);
+ /* we skip 1 */
+ CHECK_CAT1(stack[-2]);
+
+ COPY_SLOT(stack[ 0], stack[ 2]);
+ COPY_SLOT(stack[-1], stack[ 1]);
+ COPY_SLOT(stack[-2], stack[ 0]);
+ COPY_SLOT(stack[ 2], stack[-1]);
+ COPY_SLOT(stack[ 1], stack[-2]);
+ break;
+
+case ICMD_DUP2_X2: /* {STACKBASED} */
+ /* we dup either 11 or 2 */
+ if (IS_CAT1(stack[0]))
+ CHECK_CAT1(stack[-1]);
+ /* we skip either 11 or 2 */
+ if (IS_CAT1(stack[-2]))
+ CHECK_CAT1(stack[-3]);
+
+ COPY_SLOT(stack[ 0], stack[ 2]);
+ COPY_SLOT(stack[-1], stack[ 1]);
+ COPY_SLOT(stack[-2], stack[ 0]);
+ COPY_SLOT(stack[-3], stack[-1]);
+ COPY_SLOT(stack[ 2], stack[-2]);
+ COPY_SLOT(stack[ 1], stack[-3]);
+ break;
+
+
+/* this marker is needed by generate.pl: */
+/* {END_OF_CODE} */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typecheck-common.c - shared verifier code
-
- Copyright (C) 1996-2005, 2006, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-#include "vm/types.h"
-#include "vm/global.h"
-
-#include <assert.h>
-
-#include "vm/exceptions.hpp"
-#include "vm/globals.hpp"
-
-#include "vm/jit/show.hpp"
-
-#include "typecheck-common.h"
-
-
-/****************************************************************************/
-/* DEBUG HELPERS */
-/****************************************************************************/
-
-#ifdef TYPECHECK_VERBOSE_OPT
-bool opt_typecheckverbose = false;
-#endif
-
-#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
-
-void typecheck_print_var(FILE *file, jitdata *jd, s4 index)
-{
- varinfo *var;
-
- assert(index >= 0 && index < jd->varcount);
- var = VAR(index);
- typeinfo_print_type(file, var->type, &(var->typeinfo));
-}
-
-void typecheck_print_vararray(FILE *file, jitdata *jd, s4 *vars, int len)
-{
- s4 i;
-
- for (i=0; i<len; ++i) {
- if (i)
- fputc(' ', file);
- typecheck_print_var(file, jd, *vars++);
- }
-}
-
-#endif /* defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT) */
-
-
-/****************************************************************************/
-/* STATISTICS */
-/****************************************************************************/
-
-#if defined(TYPECHECK_STATISTICS)
-int stat_typechecked = 0;
-int stat_methods_with_handlers = 0;
-int stat_methods_maythrow = 0;
-int stat_iterations[STAT_ITERATIONS+1] = { 0 };
-int stat_reached = 0;
-int stat_copied = 0;
-int stat_merged = 0;
-int stat_merging_changed = 0;
-int stat_blocks[STAT_BLOCKS+1] = { 0 };
-int stat_locals[STAT_LOCALS+1] = { 0 };
-int stat_ins = 0;
-int stat_ins_maythrow = 0;
-int stat_ins_stack = 0;
-int stat_ins_field = 0;
-int stat_ins_field_unresolved = 0;
-int stat_ins_field_uninitialized = 0;
-int stat_ins_invoke = 0;
-int stat_ins_invoke_unresolved = 0;
-int stat_ins_primload = 0;
-int stat_ins_aload = 0;
-int stat_ins_builtin = 0;
-int stat_ins_builtin_gen = 0;
-int stat_ins_branch = 0;
-int stat_ins_switch = 0;
-int stat_ins_primitive_return = 0;
-int stat_ins_areturn = 0;
-int stat_ins_areturn_unresolved = 0;
-int stat_ins_athrow = 0;
-int stat_ins_athrow_unresolved = 0;
-int stat_ins_unchecked = 0;
-int stat_handlers_reached = 0;
-int stat_savedstack = 0;
-
-static void print_freq(FILE *file,int *array,int limit)
-{
- int i;
- for (i=0; i<limit; ++i)
- fprintf(file," %3d: %8d\n",i,array[i]);
- fprintf(file," >=%3d: %8d\n",limit,array[limit]);
-}
-
-void typecheck_print_statistics(FILE *file) {
- fprintf(file,"typechecked methods: %8d\n",stat_typechecked);
- fprintf(file," with handler(s): %8d\n",stat_methods_with_handlers);
- fprintf(file," with throw(s) : %8d\n",stat_methods_maythrow);
- fprintf(file,"reached blocks : %8d\n",stat_reached);
- fprintf(file,"copied states : %8d\n",stat_copied);
- fprintf(file,"merged states : %8d\n",stat_merged);
- fprintf(file,"merging changed : %8d\n",stat_merging_changed);
- fprintf(file,"handlers reached : %8d\n",stat_handlers_reached);
- fprintf(file,"saved stack (times): %8d\n",stat_savedstack);
- fprintf(file,"instructions : %8d\n",stat_ins);
- fprintf(file," stack : %8d\n",stat_ins_stack);
- fprintf(file," field access : %8d\n",stat_ins_field);
- fprintf(file," (unresolved) : %8d\n",stat_ins_field_unresolved);
- fprintf(file," (uninit.) : %8d\n",stat_ins_field_uninitialized);
- fprintf(file," invocations : %8d\n",stat_ins_invoke);
- fprintf(file," (unresolved) : %8d\n",stat_ins_invoke_unresolved);
- fprintf(file," load primitive : (currently not counted) %8d\n",stat_ins_primload);
- fprintf(file," load address : %8d\n",stat_ins_aload);
- fprintf(file," builtins : %8d\n",stat_ins_builtin);
- fprintf(file," generic : %8d\n",stat_ins_builtin_gen);
- fprintf(file," branches : %8d\n",stat_ins_branch);
- fprintf(file," switches : %8d\n",stat_ins_switch);
- fprintf(file," prim. return : %8d\n",stat_ins_primitive_return);
- fprintf(file," areturn : %8d\n",stat_ins_areturn);
- fprintf(file," (unresolved) : %8d\n",stat_ins_areturn_unresolved);
- fprintf(file," athrow : %8d\n",stat_ins_athrow);
- fprintf(file," (unresolved) : %8d\n",stat_ins_athrow_unresolved);
- fprintf(file," unchecked : %8d\n",stat_ins_unchecked);
- fprintf(file," maythrow : %8d\n",stat_ins_maythrow);
- fprintf(file,"iterations used:\n");
- print_freq(file,stat_iterations,STAT_ITERATIONS);
- fprintf(file,"basic blocks per method / 10:\n");
- print_freq(file,stat_blocks,STAT_BLOCKS);
- fprintf(file,"locals:\n");
- print_freq(file,stat_locals,STAT_LOCALS);
-}
-#endif /* defined(TYPECHECK_STATISTICS) */
-
-
-/* typecheck_init_flags ********************************************************
-
- Initialize the basic block flags for the following CFG traversal.
-
- IN:
- state............the current state of the verifier
- minflags.........minimum flags value of blocks that should be
- considered
-
-*******************************************************************************/
-
-void typecheck_init_flags(verifier_state *state, s4 minflags)
-{
- basicblock *block;
-
- /* set all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
-
- for (block = state->basicblocks; block; block = block->next) {
-
-#ifdef TYPECHECK_DEBUG
- /* check for invalid flags */
- if (block->flags != BBFINISHED && block->flags != BBDELETED && block->flags != BBUNDEF)
- {
- LOGSTR1("block flags: %d\n",block->flags); LOGFLUSH;
- TYPECHECK_ASSERT(false);
- }
-#endif
-
- if (block->flags >= minflags) {
- block->flags = BBTYPECHECK_UNDEF;
- }
- }
-
- /* the first block is always reached */
-
- if (state->basicblockcount && state->basicblocks[0].flags == BBTYPECHECK_UNDEF)
- state->basicblocks[0].flags = BBTYPECHECK_REACHED;
-}
-
-
-/* typecheck_reset_flags *******************************************************
-
- Reset the flags of basic blocks we have not reached.
-
- IN:
- state............the current state of the verifier
-
-*******************************************************************************/
-
-void typecheck_reset_flags(verifier_state *state)
-{
- basicblock *block;
-
- /* check for invalid flags at exit */
-
-#ifdef TYPECHECK_DEBUG
- for (block = state->basicblocks; block; block = block->next) {
- if (block->flags != BBDELETED
- && block->flags != BBUNDEF
- && block->flags != BBFINISHED
- && block->flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
- * some exception handlers,
- * that's ok. */
- {
- LOG2("block L%03d has invalid flags after typecheck: %d",
- block->nr,block->flags);
- TYPECHECK_ASSERT(false);
- }
- }
-#endif
-
- /* Delete blocks we never reached */
-
- for (block = state->basicblocks; block; block = block->next) {
- if (block->flags == BBTYPECHECK_UNDEF)
- block->flags = BBDELETED;
- }
-}
-
-
-/****************************************************************************/
-/* TYPESTACK MACROS AND FUNCTIONS */
-/* */
-/* These macros and functions act on the 'type stack', which is a shorthand */
-/* for the types of the stackslots of the current stack. The type of a */
-/* stack slot is usually described by a TYPE_* constant and -- for TYPE_ADR */
-/* -- by the typeinfo of the slot. The only thing that makes the type stack */
-/* more complicated are returnAddresses of local subroutines, because a */
-/* single stack slot may contain a set of more than one possible return */
-/* address. This is handled by 'return address sets'. A return address set */
-/* is kept as a linked list dangling off the typeinfo of the stack slot. */
-/****************************************************************************/
-
-/* typecheck_copy_types ********************************************************
-
- Copy the types of the source variables to the destination variables.
-
- IN:
- state............current verifier state
- srcvars..........array of variable indices to copy
- dstvars..........array of the destination variables
- n................number of variables to copy
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool typecheck_copy_types(verifier_state *state, s4 *srcvars, s4 *dstvars, s4 n)
-{
- s4 i;
- varinfo *sv;
- varinfo *dv;
- jitdata *jd = state->jd;
-
- for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
- sv = VAR(*srcvars);
- dv = VAR(*dstvars);
-
- dv->type = sv->type;
- if (dv->type == TYPE_ADR) {
- TYPEINFO_CLONE(sv->typeinfo,dv->typeinfo);
- }
- }
- return true;
-}
-
-
-/* typecheck_merge_types *******************************************************
-
- Merge the types of the source variables into the destination variables.
-
- IN:
- state............current state of the verifier
- srcvars..........source variable indices
- dstvars..........destination variable indices
- n................number of variables
-
- RETURN VALUE:
- typecheck_TRUE...the destination variables have been modified
- typecheck_FALSE..the destination variables are unchanged
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-typecheck_result typecheck_merge_types(verifier_state *state,
- s4 *srcvars,
- s4 *dstvars,
- s4 n)
-{
- s4 i;
- varinfo *sv;
- varinfo *dv;
- jitdata *jd = state->jd;
- typecheck_result r;
- bool changed = false;
-
- for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
- sv = VAR(*srcvars);
- dv = VAR(*dstvars);
-
- if (dv->type != sv->type) {
- exceptions_throw_verifyerror(state->m,"Stack type mismatch");
- return typecheck_FAIL;
- }
- if (dv->type == TYPE_ADR) {
- if (TYPEINFO_IS_PRIMITIVE(dv->typeinfo)) {
- /* dv has returnAddress type */
- if (!TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
- exceptions_throw_verifyerror(state->m,"Merging returnAddress with reference");
- return typecheck_FAIL;
- }
- }
- else {
- /* dv has reference type */
- if (TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
- exceptions_throw_verifyerror(state->m,"Merging reference with returnAddress");
- return typecheck_FAIL;
- }
- r = typeinfo_merge(state->m,&(dv->typeinfo),&(sv->typeinfo));
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
- }
- }
- }
- return changed;
-}
-
-
-/* typestate_merge *************************************************************
-
- Merge the types of one state into the destination state.
-
- IN:
- state............current state of the verifier
- dstvars..........indices of the destinations invars
- dstlocals........the destinations inlocals
- srcvars..........indices of the source's outvars
- srclocals........the source locals
- n................number of invars (== number of outvars)
-
- RETURN VALUE:
- typecheck_TRUE...destination state has been modified
- typecheck_FALSE..destination state has not been modified
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-typecheck_result typestate_merge(verifier_state *state,
- s4 *srcvars, varinfo *srclocals,
- s4 *dstvars, varinfo *dstlocals,
- s4 n)
-{
- bool changed = false;
- typecheck_result r;
-
- /* The stack is always merged. If there are returnAddresses on
- * the stack they are ignored in this step. */
-
- r = typecheck_merge_types(state, srcvars, dstvars, n);
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
-
- /* merge the locals */
-
- r = typevector_merge(state->m, dstlocals, srclocals, state->numlocals);
- if (r == typecheck_FAIL)
- return r;
- return changed | r;
-}
-
-
-/* typestate_reach *************************************************************
-
- Reach a destination block and propagate stack and local variable types
-
- IN:
- state............current state of the verifier
- destblock........destination basic block
- srcvars..........variable indices of the outvars to propagate
- srclocals........local variables to propagate
- n................number of srcvars
-
- OUT:
- state->repeat....set to true if the verifier must iterate again
- over the basic blocks
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool typestate_reach(verifier_state *state,
- basicblock *destblock,
- s4 *srcvars, varinfo *srclocals, s4 n)
-{
- varinfo *destloc;
- bool changed = false;
- typecheck_result r;
-
- LOG1("reaching block L%03d",destblock->nr);
- TYPECHECK_COUNT(stat_reached);
-
- destloc = destblock->inlocals;
-
- if (destblock->flags == BBTYPECHECK_UNDEF) {
- /* The destblock has never been reached before */
-
- TYPECHECK_COUNT(stat_copied);
- LOG1("block L%03d reached first time",destblock->nr);
-
- if (!typecheck_copy_types(state, srcvars, destblock->invars, n))
- return false;
- typevector_copy_inplace(srclocals, destloc, state->numlocals);
- changed = true;
- }
- else {
- /* The destblock has already been reached before */
-
- TYPECHECK_COUNT(stat_merged);
- LOG1("block L%03d reached before", destblock->nr);
-
- r = typestate_merge(state, srcvars, srclocals,
- destblock->invars, destblock->inlocals, n);
- if (r == typecheck_FAIL)
- return false;
- changed = r;
- TYPECHECK_COUNTIF(changed,stat_merging_changed);
- }
-
- if (changed) {
- LOG("changed!");
- destblock->flags = BBTYPECHECK_REACHED;
- if (destblock->nr <= state->bptr->nr) {
- LOG("REPEAT!");
- state->repeat = true;
- }
- }
- return true;
-}
-
-
-/* typecheck_init_locals *******************************************************
-
- Initialize the local variables in the verifier state.
-
- IN:
- state............the current state of the verifier
- newthis..........if true, mark the instance in <init> methods as
- uninitialized object.
-
- RETURN VALUE:
- true.............success,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-bool typecheck_init_locals(verifier_state *state, bool newthis)
-{
- int i;
- int varindex;
- varinfo *locals;
- varinfo *v;
- jitdata *jd = state->jd;
- int skip = 0;
-
- locals = state->basicblocks[0].inlocals;
-
- /* allocate parameter descriptors if necessary */
-
- if (!state->m->parseddesc->params)
- if (!descriptor_params_from_paramtypes(state->m->parseddesc,state->m->flags))
- return false;
-
- /* pre-initialize variables as TYPE_VOID */
-
- i = state->numlocals;
- v = locals;
- while (i--) {
- v->type = TYPE_VOID;
- v++;
- }
-
- /* if this is an instance method initialize the "this" ref type */
-
- if (!(state->m->flags & ACC_STATIC)) {
- varindex = jd->local_map[5*0 + TYPE_ADR];
- if (varindex != UNUSED) {
- if (state->validlocals < 1)
- TYPECHECK_VERIFYERROR_bool("Not enough local variables for method arguments");
- v = locals + varindex;
- v->type = TYPE_ADR;
- if (state->initmethod && newthis)
- TYPEINFO_INIT_NEWOBJECT(v->typeinfo, NULL);
- else
- typeinfo_init_classinfo(&(v->typeinfo), state->m->clazz);
- }
-
- skip = 1;
- }
-
- LOG("'this' argument set.\n");
-
- /* the rest of the arguments and the return type */
-
- if (!typeinfo_init_varinfos_from_methoddesc(locals, state->m->parseddesc,
- state->validlocals,
- skip, /* skip 'this' pointer */
- jd->local_map,
- &state->returntype))
- return false;
-
- LOG("Arguments set.\n");
- return true;
-}
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/verify/typecheck-common.c - shared verifier code
+
+ Copyright (C) 1996-2005, 2006, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+#include "vm/types.h"
+#include "vm/global.h"
+
+#include <assert.h>
+
+#include "vm/exceptions.hpp"
+#include "vm/globals.hpp"
+
+#include "vm/jit/show.hpp"
+
+#include "typecheck-common.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/****************************************************************************/
+/* DEBUG HELPERS */
+/****************************************************************************/
+
+#ifdef TYPECHECK_VERBOSE_OPT
+bool opt_typecheckverbose = false;
+#endif
+
+#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
+
+void typecheck_print_var(FILE *file, jitdata *jd, s4 index)
+{
+ varinfo *var;
+
+ assert(index >= 0 && index < jd->varcount);
+ var = VAR(index);
+ typeinfo_print_type(file, var->type, &(var->typeinfo));
+}
+
+void typecheck_print_vararray(FILE *file, jitdata *jd, s4 *vars, int len)
+{
+ s4 i;
+
+ for (i=0; i<len; ++i) {
+ if (i)
+ fputc(' ', file);
+ typecheck_print_var(file, jd, *vars++);
+ }
+}
+
+#endif /* defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT) */
+
+
+/****************************************************************************/
+/* STATISTICS */
+/****************************************************************************/
+
+#if defined(TYPECHECK_STATISTICS)
+int stat_typechecked = 0;
+int stat_methods_with_handlers = 0;
+int stat_methods_maythrow = 0;
+int stat_iterations[STAT_ITERATIONS+1] = { 0 };
+int stat_reached = 0;
+int stat_copied = 0;
+int stat_merged = 0;
+int stat_merging_changed = 0;
+int stat_blocks[STAT_BLOCKS+1] = { 0 };
+int stat_locals[STAT_LOCALS+1] = { 0 };
+int stat_ins = 0;
+int stat_ins_maythrow = 0;
+int stat_ins_stack = 0;
+int stat_ins_field = 0;
+int stat_ins_field_unresolved = 0;
+int stat_ins_field_uninitialized = 0;
+int stat_ins_invoke = 0;
+int stat_ins_invoke_unresolved = 0;
+int stat_ins_primload = 0;
+int stat_ins_aload = 0;
+int stat_ins_builtin = 0;
+int stat_ins_builtin_gen = 0;
+int stat_ins_branch = 0;
+int stat_ins_switch = 0;
+int stat_ins_primitive_return = 0;
+int stat_ins_areturn = 0;
+int stat_ins_areturn_unresolved = 0;
+int stat_ins_athrow = 0;
+int stat_ins_athrow_unresolved = 0;
+int stat_ins_unchecked = 0;
+int stat_handlers_reached = 0;
+int stat_savedstack = 0;
+
+static void print_freq(FILE *file,int *array,int limit)
+{
+ int i;
+ for (i=0; i<limit; ++i)
+ fprintf(file," %3d: %8d\n",i,array[i]);
+ fprintf(file," >=%3d: %8d\n",limit,array[limit]);
+}
+
+void typecheck_print_statistics(FILE *file) {
+ fprintf(file,"typechecked methods: %8d\n",stat_typechecked);
+ fprintf(file," with handler(s): %8d\n",stat_methods_with_handlers);
+ fprintf(file," with throw(s) : %8d\n",stat_methods_maythrow);
+ fprintf(file,"reached blocks : %8d\n",stat_reached);
+ fprintf(file,"copied states : %8d\n",stat_copied);
+ fprintf(file,"merged states : %8d\n",stat_merged);
+ fprintf(file,"merging changed : %8d\n",stat_merging_changed);
+ fprintf(file,"handlers reached : %8d\n",stat_handlers_reached);
+ fprintf(file,"saved stack (times): %8d\n",stat_savedstack);
+ fprintf(file,"instructions : %8d\n",stat_ins);
+ fprintf(file," stack : %8d\n",stat_ins_stack);
+ fprintf(file," field access : %8d\n",stat_ins_field);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_field_unresolved);
+ fprintf(file," (uninit.) : %8d\n",stat_ins_field_uninitialized);
+ fprintf(file," invocations : %8d\n",stat_ins_invoke);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_invoke_unresolved);
+ fprintf(file," load primitive : (currently not counted) %8d\n",stat_ins_primload);
+ fprintf(file," load address : %8d\n",stat_ins_aload);
+ fprintf(file," builtins : %8d\n",stat_ins_builtin);
+ fprintf(file," generic : %8d\n",stat_ins_builtin_gen);
+ fprintf(file," branches : %8d\n",stat_ins_branch);
+ fprintf(file," switches : %8d\n",stat_ins_switch);
+ fprintf(file," prim. return : %8d\n",stat_ins_primitive_return);
+ fprintf(file," areturn : %8d\n",stat_ins_areturn);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_areturn_unresolved);
+ fprintf(file," athrow : %8d\n",stat_ins_athrow);
+ fprintf(file," (unresolved) : %8d\n",stat_ins_athrow_unresolved);
+ fprintf(file," unchecked : %8d\n",stat_ins_unchecked);
+ fprintf(file," maythrow : %8d\n",stat_ins_maythrow);
+ fprintf(file,"iterations used:\n");
+ print_freq(file,stat_iterations,STAT_ITERATIONS);
+ fprintf(file,"basic blocks per method / 10:\n");
+ print_freq(file,stat_blocks,STAT_BLOCKS);
+ fprintf(file,"locals:\n");
+ print_freq(file,stat_locals,STAT_LOCALS);
+}
+#endif /* defined(TYPECHECK_STATISTICS) */
+
+
+/* typecheck_init_flags ********************************************************
+
+ Initialize the basic block flags for the following CFG traversal.
+
+ IN:
+ state............the current state of the verifier
+ minflags.........minimum flags value of blocks that should be
+ considered
+
+*******************************************************************************/
+
+void typecheck_init_flags(verifier_state *state, s4 minflags)
+{
+ basicblock *block;
+
+ /* set all BBFINISHED blocks to BBTYPECHECK_UNDEF. */
+
+ for (block = state->basicblocks; block; block = block->next) {
+
+#ifdef TYPECHECK_DEBUG
+ /* check for invalid flags */
+ if (block->flags != BBFINISHED && block->flags != BBDELETED && block->flags != BBUNDEF)
+ {
+ LOGSTR1("block flags: %d\n",block->flags); LOGFLUSH;
+ TYPECHECK_ASSERT(false);
+ }
+#endif
+
+ if (block->flags >= minflags) {
+ block->flags = BBTYPECHECK_UNDEF;
+ }
+ }
+
+ /* the first block is always reached */
+
+ if (state->basicblockcount && state->basicblocks[0].flags == BBTYPECHECK_UNDEF)
+ state->basicblocks[0].flags = BBTYPECHECK_REACHED;
+}
+
+
+/* typecheck_reset_flags *******************************************************
+
+ Reset the flags of basic blocks we have not reached.
+
+ IN:
+ state............the current state of the verifier
+
+*******************************************************************************/
+
+void typecheck_reset_flags(verifier_state *state)
+{
+ basicblock *block;
+
+ /* check for invalid flags at exit */
+
+#ifdef TYPECHECK_DEBUG
+ for (block = state->basicblocks; block; block = block->next) {
+ if (block->flags != BBDELETED
+ && block->flags != BBUNDEF
+ && block->flags != BBFINISHED
+ && block->flags != BBTYPECHECK_UNDEF) /* typecheck may never reach
+ * some exception handlers,
+ * that's ok. */
+ {
+ LOG2("block L%03d has invalid flags after typecheck: %d",
+ block->nr,block->flags);
+ TYPECHECK_ASSERT(false);
+ }
+ }
+#endif
+
+ /* Delete blocks we never reached */
+
+ for (block = state->basicblocks; block; block = block->next) {
+ if (block->flags == BBTYPECHECK_UNDEF)
+ block->flags = BBDELETED;
+ }
+}
+
+
+/****************************************************************************/
+/* TYPESTACK MACROS AND FUNCTIONS */
+/* */
+/* These macros and functions act on the 'type stack', which is a shorthand */
+/* for the types of the stackslots of the current stack. The type of a */
+/* stack slot is usually described by a TYPE_* constant and -- for TYPE_ADR */
+/* -- by the typeinfo of the slot. The only thing that makes the type stack */
+/* more complicated are returnAddresses of local subroutines, because a */
+/* single stack slot may contain a set of more than one possible return */
+/* address. This is handled by 'return address sets'. A return address set */
+/* is kept as a linked list dangling off the typeinfo of the stack slot. */
+/****************************************************************************/
+
+/* typecheck_copy_types ********************************************************
+
+ Copy the types of the source variables to the destination variables.
+
+ IN:
+ state............current verifier state
+ srcvars..........array of variable indices to copy
+ dstvars..........array of the destination variables
+ n................number of variables to copy
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool typecheck_copy_types(verifier_state *state, s4 *srcvars, s4 *dstvars, s4 n)
+{
+ s4 i;
+ varinfo *sv;
+ varinfo *dv;
+ jitdata *jd = state->jd;
+
+ for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
+ sv = VAR(*srcvars);
+ dv = VAR(*dstvars);
+
+ dv->type = sv->type;
+ if (dv->type == TYPE_ADR) {
+ TYPEINFO_CLONE(sv->typeinfo,dv->typeinfo);
+ }
+ }
+ return true;
+}
+
+
+/* typecheck_merge_types *******************************************************
+
+ Merge the types of the source variables into the destination variables.
+
+ IN:
+ state............current state of the verifier
+ srcvars..........source variable indices
+ dstvars..........destination variable indices
+ n................number of variables
+
+ RETURN VALUE:
+ typecheck_TRUE...the destination variables have been modified
+ typecheck_FALSE..the destination variables are unchanged
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+typecheck_result typecheck_merge_types(verifier_state *state,
+ s4 *srcvars,
+ s4 *dstvars,
+ s4 n)
+{
+ s4 i;
+ varinfo *sv;
+ varinfo *dv;
+ jitdata *jd = state->jd;
+ typecheck_result r;
+ bool changed = false;
+
+ for (i=0; i < n; ++i, ++srcvars, ++dstvars) {
+ sv = VAR(*srcvars);
+ dv = VAR(*dstvars);
+
+ if (dv->type != sv->type) {
+ exceptions_throw_verifyerror(state->m,"Stack type mismatch");
+ return typecheck_FAIL;
+ }
+ if (dv->type == TYPE_ADR) {
+ if (TYPEINFO_IS_PRIMITIVE(dv->typeinfo)) {
+ /* dv has returnAddress type */
+ if (!TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
+ exceptions_throw_verifyerror(state->m,"Merging returnAddress with reference");
+ return typecheck_FAIL;
+ }
+ }
+ else {
+ /* dv has reference type */
+ if (TYPEINFO_IS_PRIMITIVE(sv->typeinfo)) {
+ exceptions_throw_verifyerror(state->m,"Merging reference with returnAddress");
+ return typecheck_FAIL;
+ }
+ r = typeinfo_merge(state->m,&(dv->typeinfo),&(sv->typeinfo));
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+ }
+ }
+ }
+ return (typecheck_result) changed;
+}
+
+
+/* typestate_merge *************************************************************
+
+ Merge the types of one state into the destination state.
+
+ IN:
+ state............current state of the verifier
+ dstvars..........indices of the destinations invars
+ dstlocals........the destinations inlocals
+ srcvars..........indices of the source's outvars
+ srclocals........the source locals
+ n................number of invars (== number of outvars)
+
+ RETURN VALUE:
+ typecheck_TRUE...destination state has been modified
+ typecheck_FALSE..destination state has not been modified
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+typecheck_result typestate_merge(verifier_state *state,
+ s4 *srcvars, varinfo *srclocals,
+ s4 *dstvars, varinfo *dstlocals,
+ s4 n)
+{
+ bool changed = false;
+ typecheck_result r;
+
+ /* The stack is always merged. If there are returnAddresses on
+ * the stack they are ignored in this step. */
+
+ r = typecheck_merge_types(state, srcvars, dstvars, n);
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+
+ /* merge the locals */
+
+ r = typevector_merge(state->m, dstlocals, srclocals, state->numlocals);
+ if (r == typecheck_FAIL)
+ return r;
+ return (typecheck_result) (changed | r);
+}
+
+
+/* typestate_reach *************************************************************
+
+ Reach a destination block and propagate stack and local variable types
+
+ IN:
+ state............current state of the verifier
+ destblock........destination basic block
+ srcvars..........variable indices of the outvars to propagate
+ srclocals........local variables to propagate
+ n................number of srcvars
+
+ OUT:
+ state->repeat....set to true if the verifier must iterate again
+ over the basic blocks
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool typestate_reach(verifier_state *state,
+ basicblock *destblock,
+ s4 *srcvars, varinfo *srclocals, s4 n)
+{
+ varinfo *destloc;
+ bool changed = false;
+ typecheck_result r;
+
+ LOG1("reaching block L%03d",destblock->nr);
+ TYPECHECK_COUNT(stat_reached);
+
+ destloc = destblock->inlocals;
+
+ if (destblock->flags == BBTYPECHECK_UNDEF) {
+ /* The destblock has never been reached before */
+
+ TYPECHECK_COUNT(stat_copied);
+ LOG1("block L%03d reached first time",destblock->nr);
+
+ if (!typecheck_copy_types(state, srcvars, destblock->invars, n))
+ return false;
+ typevector_copy_inplace(srclocals, destloc, state->numlocals);
+ changed = true;
+ }
+ else {
+ /* The destblock has already been reached before */
+
+ TYPECHECK_COUNT(stat_merged);
+ LOG1("block L%03d reached before", destblock->nr);
+
+ r = typestate_merge(state, srcvars, srclocals,
+ destblock->invars, destblock->inlocals, n);
+ if (r == typecheck_FAIL)
+ return false;
+ changed = r;
+ TYPECHECK_COUNTIF(changed,stat_merging_changed);
+ }
+
+ if (changed) {
+ LOG("changed!");
+ destblock->flags = BBTYPECHECK_REACHED;
+ if (destblock->nr <= state->bptr->nr) {
+ LOG("REPEAT!");
+ state->repeat = true;
+ }
+ }
+ return true;
+}
+
+
+/* typecheck_init_locals *******************************************************
+
+ Initialize the local variables in the verifier state.
+
+ IN:
+ state............the current state of the verifier
+ newthis..........if true, mark the instance in <init> methods as
+ uninitialized object.
+
+ RETURN VALUE:
+ true.............success,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+bool typecheck_init_locals(verifier_state *state, bool newthis)
+{
+ int i;
+ int varindex;
+ varinfo *locals;
+ varinfo *v;
+ jitdata *jd = state->jd;
+ int skip = 0;
+
+ locals = state->basicblocks[0].inlocals;
+
+ /* allocate parameter descriptors if necessary */
+
+ if (!state->m->parseddesc->params)
+ if (!descriptor_params_from_paramtypes(state->m->parseddesc,state->m->flags))
+ return false;
+
+ /* pre-initialize variables as TYPE_VOID */
+
+ i = state->numlocals;
+ v = locals;
+ while (i--) {
+ v->type = TYPE_VOID;
+ v++;
+ }
+
+ /* if this is an instance method initialize the "this" ref type */
+
+ if (!(state->m->flags & ACC_STATIC)) {
+ varindex = jd->local_map[5*0 + TYPE_ADR];
+ if (varindex != UNUSED) {
+ if (state->validlocals < 1)
+ TYPECHECK_VERIFYERROR_bool("Not enough local variables for method arguments");
+ v = locals + varindex;
+ v->type = TYPE_ADR;
+ if (state->initmethod && newthis)
+ TYPEINFO_INIT_NEWOBJECT(v->typeinfo, NULL);
+ else
+ typeinfo_init_classinfo(&(v->typeinfo), state->m->clazz);
+ }
+
+ skip = 1;
+ }
+
+ LOG("'this' argument set.\n");
+
+ /* the rest of the arguments and the return type */
+
+ if (!typeinfo_init_varinfos_from_methoddesc(locals, state->m->parseddesc,
+ state->validlocals,
+ skip, /* skip 'this' pointer */
+ jd->local_map,
+ &state->returntype))
+ return false;
+
+ LOG("Arguments set.\n");
+ return true;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typecheck-common.h - internal header for the type checker
-
- Copyright (C) 1996-2005, 2006, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _TYPECHECK_COMMON_H
-#define _TYPECHECK_COMMON_H
-
-#include "config.h"
-#include "vm/types.h"
-#include "vm/global.h"
-
-#include <assert.h>
-
-#include "vm/jit/jit.hpp"
-
-
-/****************************************************************************/
-/* DEBUG HELPERS */
-/****************************************************************************/
-
-#ifdef TYPECHECK_DEBUG
-#define TYPECHECK_ASSERT(cond) assert(cond)
-#else
-#define TYPECHECK_ASSERT(cond)
-#endif
-
-#ifdef TYPECHECK_VERBOSE_OPT
-extern bool opt_typecheckverbose;
-#define DOLOG(action) do { if (opt_typecheckverbose) {action;} } while(0)
-#else
-#define DOLOG(action)
-#endif
-
-#ifdef TYPECHECK_VERBOSE
-#define TYPECHECK_VERBOSE_IMPORTANT
-#define LOGNL DOLOG(puts(""))
-#define LOG(str) DOLOG(puts(str);)
-#define LOG1(str,a) DOLOG(printf(str,a); LOGNL)
-#define LOG2(str,a,b) DOLOG(printf(str,a,b); LOGNL)
-#define LOG3(str,a,b,c) DOLOG(printf(str,a,b,c); LOGNL)
-#define LOGIF(cond,str) DOLOG(do {if (cond) { puts(str); }} while(0))
-#ifdef TYPEINFO_DEBUG
-#define LOGINFO(info) DOLOG(do {typeinfo_print_short(stdout,(info)); LOGNL;} while(0))
-#else
-#define LOGINFO(info)
-#define typevector_print(x,y,z)
-#endif
-#define LOGFLUSH DOLOG(fflush(stdout))
-#define LOGSTR(str) DOLOG(printf("%s", str))
-#define LOGSTR1(str,a) DOLOG(printf(str,a))
-#define LOGSTR2(str,a,b) DOLOG(printf(str,a,b))
-#define LOGSTR3(str,a,b,c) DOLOG(printf(str,a,b,c))
-#define LOGNAME(c) DOLOG(class_classref_or_classinfo_print(c))
-#define LOGMETHOD(str,m) DOLOG(printf("%s", str); method_println(m);)
-#else
-#define LOG(str)
-#define LOG1(str,a)
-#define LOG2(str,a,b)
-#define LOG3(str,a,b,c)
-#define LOGIF(cond,str)
-#define LOGINFO(info)
-#define LOGFLUSH
-#define LOGNL
-#define LOGSTR(str)
-#define LOGSTR1(str,a)
-#define LOGSTR2(str,a,b)
-#define LOGSTR3(str,a,b,c)
-#define LOGNAME(c)
-#define LOGMETHOD(str,m)
-#endif
-
-#ifdef TYPECHECK_VERBOSE_IMPORTANT
-#define LOGimp(str) DOLOG(puts(str);LOGNL)
-#define LOGimpSTR(str) DOLOG(puts(str))
-#else
-#define LOGimp(str)
-#define LOGimpSTR(str)
-#endif
-
-#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
-#include <stdio.h>
-void typecheck_print_var(FILE *file, jitdata *jd, s4 index);
-void typecheck_print_vararray(FILE *file, jitdata *jd, s4 *vars, int len);
-#endif
-
-
-/****************************************************************************/
-/* STATISTICS */
-/****************************************************************************/
-
-#if defined(TYPECHECK_DEBUG) && !defined(TYPECHECK_NO_STATISTICS)
-/*#define TYPECHECK_STATISTICS*/
-#endif
-
-#ifdef TYPECHECK_STATISTICS
-#define STAT_ITERATIONS 10
-#define STAT_BLOCKS 10
-#define STAT_LOCALS 16
-
-extern int stat_typechecked;
-extern int stat_methods_with_handlers;
-extern int stat_methods_maythrow;
-extern int stat_iterations[STAT_ITERATIONS+1];
-extern int stat_reached;
-extern int stat_copied;
-extern int stat_merged;
-extern int stat_merging_changed;
-extern int stat_blocks[STAT_BLOCKS+1];
-extern int stat_locals[STAT_LOCALS+1];
-extern int stat_ins;
-extern int stat_ins_maythrow;
-extern int stat_ins_stack;
-extern int stat_ins_field;
-extern int stat_ins_field_unresolved;
-extern int stat_ins_field_uninitialized;
-extern int stat_ins_invoke;
-extern int stat_ins_invoke_unresolved;
-extern int stat_ins_primload;
-extern int stat_ins_aload;
-extern int stat_ins_builtin;
-extern int stat_ins_builtin_gen;
-extern int stat_ins_branch;
-extern int stat_ins_switch;
-extern int stat_ins_primitive_return;
-extern int stat_ins_areturn;
-extern int stat_ins_areturn_unresolved;
-extern int stat_ins_athrow;
-extern int stat_ins_athrow_unresolved;
-extern int stat_ins_unchecked;
-extern int stat_handlers_reached;
-extern int stat_savedstack;
-
-#define TYPECHECK_MARK(var) ((var) = true)
-#define TYPECHECK_COUNT(cnt) (cnt)++
-#define TYPECHECK_COUNTIF(cond,cnt) do{if(cond) (cnt)++;} while(0)
-#define TYPECHECK_COUNT_FREQ(array,val,limit) \
- do { \
- if ((val) < (limit)) (array)[val]++; \
- else (array)[limit]++; \
- } while (0)
-
-void typecheck_print_statistics(FILE *file);
-
-#else /* !defined(TYPECHECK_STATISTICS) */
-
-#define TYPECHECK_COUNT(cnt)
-#define TYPECHECK_MARK(var)
-#define TYPECHECK_COUNTIF(cond,cnt)
-#define TYPECHECK_COUNT_FREQ(array,val,limit)
-
-#endif /* defined(TYPECHECK_STATISTICS) */
-
-
-/****************************************************************************/
-/* MACROS FOR THROWING EXCEPTIONS */
-/****************************************************************************/
-
-#define TYPECHECK_VERIFYERROR_ret(m,msg,retval) \
- do { \
- exceptions_throw_verifyerror((m), (msg)); \
- return (retval); \
- } while (0)
-
-#define TYPECHECK_VERIFYERROR_main(msg) TYPECHECK_VERIFYERROR_ret(state.m,(msg),NULL)
-#define TYPECHECK_VERIFYERROR_bool(msg) TYPECHECK_VERIFYERROR_ret(state->m,(msg),false)
-
-
-/****************************************************************************/
-/* MISC MACROS */
-/****************************************************************************/
-
-#define COPYTYPE(source,dest) \
- {if (VAROP(source)->type == TYPE_ADR) \
- TYPEINFO_COPY(VAROP(source)->typeinfo,VAROP(dest)->typeinfo);}
-
-
-/****************************************************************************/
-/* JSR VERIFICATION (stack-based verifier) */
-/****************************************************************************/
-
-typedef struct typecheck_jsr_t typecheck_jsr_t;
-typedef struct typecheck_jsr_caller_t typecheck_jsr_caller_t;
-
-struct typecheck_jsr_caller_t {
- typecheck_jsr_caller_t *next; /* next in linked list */
- basicblock *callblock; /* block containing the calling JSR */
-};
-
-struct typecheck_jsr_t {
- typecheck_jsr_t *next; /* next (lower) in the call chain */
- basicblock *start; /* for debugging */
- typecheck_jsr_caller_t *callers; /* list of callers (blocks with JSR) */
- basicblock *retblock; /* block with the RET for this sub */
- bool active; /* true if this sub is currently active */
- char *blockflags; /* saved block flags when JSR was traversed */
- char *usedlocals; /* != 0 for each local used in this sub */
- typedescriptor_t *retlocals; /* locals on the RET edge */
- typedescriptor_t *retstack; /* stack on the RET edge */
- s4 retdepth; /* stack depth on the RET edge */
-};
-
-/****************************************************************************/
-/* VERIFIER STATE STRUCT */
-/****************************************************************************/
-
-/* verifier_state - This structure keeps the current state of the */
-/* bytecode verifier for passing it between verifier functions. */
-
-typedef struct verifier_state {
- instruction *iptr; /* pointer to current instruction */
- basicblock *bptr; /* pointer to current basic block */
-
- methodinfo *m; /* the current method */
- jitdata *jd; /* jitdata for current method */
- codegendata *cd; /* codegendata for current method */
-
- basicblock *basicblocks;
- s4 basicblockcount;
-
- s4 numlocals; /* number of local variables */
- s4 validlocals; /* number of Java-accessible locals */
-
- typedescriptor_t returntype; /* return type of the current method */
-
- s4 *savedindices;
- s4 *savedinvars; /* saved invar pointer */
-
- s4 exinvars;
-
- exception_entry **handlers; /* active exception handlers */
-
- bool repeat; /* if true, blocks are iterated over again */
- bool initmethod; /* true if this is an "<init>" method */
-
-#ifdef TYPECHECK_STATISTICS
- bool stat_maythrow; /* at least one instruction may throw */
-#endif
-
- /* the following fields are used by the stackbased verifier only: */
-
- typedescriptor_t *locals; /* current local variables */
- typedescriptor_t *startlocals;/* locals at the start of each block */
- typedescriptor_t *startstack; /* stack at the start of each block */
- s4 *indepth; /* stack depth at --''-- */
- typedescriptor_t *stackceiling; /* upper edge of verifier stack */
-
- typecheck_jsr_t *topjsr; /* most recently called subroutine */
- typecheck_jsr_t **jsrinfos; /* subroutine info for each block */
-} verifier_state;
-
-void typecheck_init_flags(verifier_state *state, s4 minflags);
-void typecheck_reset_flags(verifier_state *state);
-
-bool typecheck_copy_types(verifier_state *state,
- s4 *srcvars, s4 *dstvars, s4 n);
-
-typecheck_result typecheck_merge_types(verifier_state *state,
- s4 *srcvars,
- s4 *dstvars,
- s4 n);
-
-typecheck_result typestate_merge(verifier_state *state,
- s4 *srcvars, varinfo *srclocals,
- s4 *dstvars, varinfo *dstlocals,
- s4 n);
-
-bool typestate_reach(verifier_state *state,
- basicblock *destblock,
- s4 *srcvars, varinfo *srclocals, s4 n);
-
-bool typecheck_init_locals(verifier_state *state, bool newthis);
-
-#endif /* _TYPECHECK_COMMON_H */
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/verify/typecheck-common.h - internal header for the type checker
+
+ Copyright (C) 1996-2005, 2006, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _TYPECHECK_COMMON_H
+#define _TYPECHECK_COMMON_H
+
+#include "config.h"
+#include "vm/types.h"
+#include "vm/global.h"
+
+#include <assert.h>
+
+#include "vm/jit/jit.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/****************************************************************************/
+/* DEBUG HELPERS */
+/****************************************************************************/
+
+#ifdef TYPECHECK_DEBUG
+#define TYPECHECK_ASSERT(cond) assert(cond)
+#else
+#define TYPECHECK_ASSERT(cond)
+#endif
+
+#ifdef TYPECHECK_VERBOSE_OPT
+extern bool opt_typecheckverbose;
+#define DOLOG(action) do { if (opt_typecheckverbose) {action;} } while(0)
+#else
+#define DOLOG(action)
+#endif
+
+#ifdef TYPECHECK_VERBOSE
+#define TYPECHECK_VERBOSE_IMPORTANT
+#define LOGNL DOLOG(puts(""))
+#define LOG(str) DOLOG(puts(str);)
+#define LOG1(str,a) DOLOG(printf(str,a); LOGNL)
+#define LOG2(str,a,b) DOLOG(printf(str,a,b); LOGNL)
+#define LOG3(str,a,b,c) DOLOG(printf(str,a,b,c); LOGNL)
+#define LOGIF(cond,str) DOLOG(do {if (cond) { puts(str); }} while(0))
+#ifdef TYPEINFO_DEBUG
+#define LOGINFO(info) DOLOG(do {typeinfo_print_short(stdout,(info)); LOGNL;} while(0))
+#else
+#define LOGINFO(info)
+#define typevector_print(x,y,z)
+#endif
+#define LOGFLUSH DOLOG(fflush(stdout))
+#define LOGSTR(str) DOLOG(printf("%s", str))
+#define LOGSTR1(str,a) DOLOG(printf(str,a))
+#define LOGSTR2(str,a,b) DOLOG(printf(str,a,b))
+#define LOGSTR3(str,a,b,c) DOLOG(printf(str,a,b,c))
+#define LOGNAME(c) DOLOG(class_classref_or_classinfo_print(c))
+#define LOGMETHOD(str,m) DOLOG(printf("%s", str); method_println(m);)
+#else
+#define LOG(str)
+#define LOG1(str,a)
+#define LOG2(str,a,b)
+#define LOG3(str,a,b,c)
+#define LOGIF(cond,str)
+#define LOGINFO(info)
+#define LOGFLUSH
+#define LOGNL
+#define LOGSTR(str)
+#define LOGSTR1(str,a)
+#define LOGSTR2(str,a,b)
+#define LOGSTR3(str,a,b,c)
+#define LOGNAME(c)
+#define LOGMETHOD(str,m)
+#endif
+
+#ifdef TYPECHECK_VERBOSE_IMPORTANT
+#define LOGimp(str) DOLOG(puts(str);LOGNL)
+#define LOGimpSTR(str) DOLOG(puts(str))
+#else
+#define LOGimp(str)
+#define LOGimpSTR(str)
+#endif
+
+#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
+#include <stdio.h>
+void typecheck_print_var(FILE *file, jitdata *jd, s4 index);
+void typecheck_print_vararray(FILE *file, jitdata *jd, s4 *vars, int len);
+#endif
+
+
+/****************************************************************************/
+/* STATISTICS */
+/****************************************************************************/
+
+#if defined(TYPECHECK_DEBUG) && !defined(TYPECHECK_NO_STATISTICS)
+/*#define TYPECHECK_STATISTICS*/
+#endif
+
+#ifdef TYPECHECK_STATISTICS
+#define STAT_ITERATIONS 10
+#define STAT_BLOCKS 10
+#define STAT_LOCALS 16
+
+extern int stat_typechecked;
+extern int stat_methods_with_handlers;
+extern int stat_methods_maythrow;
+extern int stat_iterations[STAT_ITERATIONS+1];
+extern int stat_reached;
+extern int stat_copied;
+extern int stat_merged;
+extern int stat_merging_changed;
+extern int stat_blocks[STAT_BLOCKS+1];
+extern int stat_locals[STAT_LOCALS+1];
+extern int stat_ins;
+extern int stat_ins_maythrow;
+extern int stat_ins_stack;
+extern int stat_ins_field;
+extern int stat_ins_field_unresolved;
+extern int stat_ins_field_uninitialized;
+extern int stat_ins_invoke;
+extern int stat_ins_invoke_unresolved;
+extern int stat_ins_primload;
+extern int stat_ins_aload;
+extern int stat_ins_builtin;
+extern int stat_ins_builtin_gen;
+extern int stat_ins_branch;
+extern int stat_ins_switch;
+extern int stat_ins_primitive_return;
+extern int stat_ins_areturn;
+extern int stat_ins_areturn_unresolved;
+extern int stat_ins_athrow;
+extern int stat_ins_athrow_unresolved;
+extern int stat_ins_unchecked;
+extern int stat_handlers_reached;
+extern int stat_savedstack;
+
+#define TYPECHECK_MARK(var) ((var) = true)
+#define TYPECHECK_COUNT(cnt) (cnt)++
+#define TYPECHECK_COUNTIF(cond,cnt) do{if(cond) (cnt)++;} while(0)
+#define TYPECHECK_COUNT_FREQ(array,val,limit) \
+ do { \
+ if ((val) < (limit)) (array)[val]++; \
+ else (array)[limit]++; \
+ } while (0)
+
+void typecheck_print_statistics(FILE *file);
+
+#else /* !defined(TYPECHECK_STATISTICS) */
+
+#define TYPECHECK_COUNT(cnt)
+#define TYPECHECK_MARK(var)
+#define TYPECHECK_COUNTIF(cond,cnt)
+#define TYPECHECK_COUNT_FREQ(array,val,limit)
+
+#endif /* defined(TYPECHECK_STATISTICS) */
+
+
+/****************************************************************************/
+/* MACROS FOR THROWING EXCEPTIONS */
+/****************************************************************************/
+
+#define TYPECHECK_VERIFYERROR_ret(m,msg,retval) \
+ do { \
+ exceptions_throw_verifyerror((m), (msg)); \
+ return (retval); \
+ } while (0)
+
+#define TYPECHECK_VERIFYERROR_main(msg) TYPECHECK_VERIFYERROR_ret(state.m,(msg),NULL)
+#define TYPECHECK_VERIFYERROR_bool(msg) TYPECHECK_VERIFYERROR_ret(state->m,(msg),false)
+
+
+/****************************************************************************/
+/* MISC MACROS */
+/****************************************************************************/
+
+#define COPYTYPE(source,dest) \
+ {if (VAROP(source)->type == TYPE_ADR) \
+ TYPEINFO_COPY(VAROP(source)->typeinfo,VAROP(dest)->typeinfo);}
+
+
+/****************************************************************************/
+/* JSR VERIFICATION (stack-based verifier) */
+/****************************************************************************/
+
+typedef struct typecheck_jsr_t typecheck_jsr_t;
+typedef struct typecheck_jsr_caller_t typecheck_jsr_caller_t;
+
+struct typecheck_jsr_caller_t {
+ typecheck_jsr_caller_t *next; /* next in linked list */
+ basicblock *callblock; /* block containing the calling JSR */
+};
+
+struct typecheck_jsr_t {
+ typecheck_jsr_t *next; /* next (lower) in the call chain */
+ basicblock *start; /* for debugging */
+ typecheck_jsr_caller_t *callers; /* list of callers (blocks with JSR) */
+ basicblock *retblock; /* block with the RET for this sub */
+ bool active; /* true if this sub is currently active */
+ char *blockflags; /* saved block flags when JSR was traversed */
+ char *usedlocals; /* != 0 for each local used in this sub */
+ typedescriptor_t *retlocals; /* locals on the RET edge */
+ typedescriptor_t *retstack; /* stack on the RET edge */
+ s4 retdepth; /* stack depth on the RET edge */
+};
+
+/****************************************************************************/
+/* VERIFIER STATE STRUCT */
+/****************************************************************************/
+
+/* verifier_state - This structure keeps the current state of the */
+/* bytecode verifier for passing it between verifier functions. */
+
+typedef struct verifier_state {
+ instruction *iptr; /* pointer to current instruction */
+ basicblock *bptr; /* pointer to current basic block */
+
+ methodinfo *m; /* the current method */
+ jitdata *jd; /* jitdata for current method */
+ codegendata *cd; /* codegendata for current method */
+
+ basicblock *basicblocks;
+ s4 basicblockcount;
+
+ s4 numlocals; /* number of local variables */
+ s4 validlocals; /* number of Java-accessible locals */
+
+ typedescriptor_t returntype; /* return type of the current method */
+
+ s4 *savedindices;
+ s4 *savedinvars; /* saved invar pointer */
+
+ s4 exinvars;
+
+ exception_entry **handlers; /* active exception handlers */
+
+ bool repeat; /* if true, blocks are iterated over again */
+ bool initmethod; /* true if this is an "<init>" method */
+
+#ifdef TYPECHECK_STATISTICS
+ bool stat_maythrow; /* at least one instruction may throw */
+#endif
+
+ /* the following fields are used by the stackbased verifier only: */
+
+ typedescriptor_t *locals; /* current local variables */
+ typedescriptor_t *startlocals;/* locals at the start of each block */
+ typedescriptor_t *startstack; /* stack at the start of each block */
+ s4 *indepth; /* stack depth at --''-- */
+ typedescriptor_t *stackceiling; /* upper edge of verifier stack */
+
+ typecheck_jsr_t *topjsr; /* most recently called subroutine */
+ typecheck_jsr_t **jsrinfos; /* subroutine info for each block */
+} verifier_state;
+
+void typecheck_init_flags(verifier_state *state, s4 minflags);
+void typecheck_reset_flags(verifier_state *state);
+
+bool typecheck_copy_types(verifier_state *state,
+ s4 *srcvars, s4 *dstvars, s4 n);
+
+typecheck_result typecheck_merge_types(verifier_state *state,
+ s4 *srcvars,
+ s4 *dstvars,
+ s4 n);
+
+typecheck_result typestate_merge(verifier_state *state,
+ s4 *srcvars, varinfo *srclocals,
+ s4 *dstvars, varinfo *dstlocals,
+ s4 n);
+
+bool typestate_reach(verifier_state *state,
+ basicblock *destblock,
+ s4 *srcvars, varinfo *srclocals, s4 n);
+
+bool typecheck_init_locals(verifier_state *state, bool newthis);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _TYPECHECK_COMMON_H */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typecheck-stackbased.c - stack-based verifier
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-
-#include "vm/types.h"
-
-#include "vm/jit/builtin.hpp"
-#include "mm/memory.h"
-
-#include "vm/array.hpp"
-#include "vm/exceptions.hpp"
-#include "vm/global.h"
-#include "vm/globals.hpp"
-#include "vm/primitive.hpp"
-
-#include "vm/jit/parse.h"
-#include "vm/jit/show.hpp"
-#include "vm/jit/stack.h"
-#include "vm/jit/verify/typecheck-common.h"
-
-
-/* this #if runs over the whole file: */
-#if defined(ENABLE_VERIFIER)
-
-typedef typedescriptor_t verifier_slot_t;
-
-#if defined(TYPECHECK_VERBOSE)
-static void typecheck_stackbased_show_state(verifier_state *state,
- typedescriptor *stack,
- typedescriptor *stackfloor,
- bool showins);
-#endif
-
-
-#define CHECK_STACK_DEPTH(d) \
- if (((u1*)stack - (u1*)stackfloor) \
- < (((d)-1) * (int)sizeof(verifier_slot_t))) \
- goto throw_stack_underflow;
-
-/* XXX don't need to check against ACONST for every ICMD */
-#define CHECK_STACK_SPACE(d) \
- if (((u1*)STATE->stackceiling - (u1*)stack) \
- < (((d)+1) * (int)sizeof(verifier_slot_t))) \
- if (STATE->iptr->opc != ICMD_ACONST \
- || INSTRUCTION_MUST_CHECK(STATE->iptr)) \
- goto throw_stack_overflow;
-
-#define CHECK_STACK_TYPE(s, t) \
- if ((s).type != (t)) \
- goto throw_stack_type_error;
-
-/* XXX inefficient */
-#define CHECK_LOCAL_TYPE(index, t) \
- do { \
- if (state.locals[(index)].type != (t)) \
- goto throw_local_type_error; \
- if (STATE->topjsr) \
- STATE->topjsr->usedlocals[(index)] = 1; \
- if (STATE->topjsr && IS_2_WORD_TYPE(t)) \
- STATE->topjsr->usedlocals[(index) + 1] = 1; \
- } while(0)
-
-/* XXX inefficient */
-#define STORE_LOCAL(t, index) \
- do { \
- state.locals[(index)].type = (t); \
- if ((index) && IS_2_WORD_TYPE(state.locals[(index)-1].type)) \
- state.locals[(index-1)].type = TYPE_VOID; \
- if (STATE->topjsr) \
- STATE->topjsr->usedlocals[(index)] = 1; \
- } while (0)
-
-/* XXX inefficient */
-#define STORE_LOCAL_2_WORD(t, index) \
- do { \
- STORE_LOCAL(t, index); \
- state.locals[(index)+1].type = TYPE_VOID; \
- if (STATE->topjsr) \
- STATE->topjsr->usedlocals[(index)] = 1; \
- } while (0)
-
-#define VERIFY_ERROR(msg) \
- do { \
- LOG1("VerifyError: %s", msg); \
- exceptions_throw_verifyerror(STATE->m, msg); \
- return false; \
- } while (0)
-
-#define IS_CAT1(slot) \
- ((slot).type != TYPE_VOID && !IS_2_WORD_TYPE((slot).type))
-
-#define IS_CAT2(slot) \
- ((slot).type != TYPE_VOID && IS_2_WORD_TYPE((slot).type))
-
-#define CHECK_CAT1(slot) \
- do { \
- if (!IS_CAT1(slot)) \
- goto throw_stack_category_error; \
- } while (0)
-
-#define CHECK_CAT2(slot) \
- do { \
- if (!IS_CAT2(slot)) \
- goto throw_stack_category_error; \
- } while (0)
-
-#define COPY_SLOT(s, d) \
- do { (d) = (s); } while (0)
-
-#define REACH_BLOCK(target) \
- do { \
- if (!typecheck_stackbased_reach(STATE, (target), stack, \
- (stack - stackfloor) + 1)) \
- return false; \
- } while (0)
-
-#define REACH(target) \
- do { \
- REACH_BLOCK((target).block); \
- } while (0)
-
-#undef TYPECHECK_INT
-#undef TYPECHECK_LNG
-#undef TYPECHECK_FLT
-#undef TYPECHECK_DBL
-#undef TYPECHECK_ADR
-
-/* XXX should reuse typevector code */
-static typecheck_result typecheck_stackbased_merge_locals(methodinfo *m,
- typedescriptor_t *dst,
- typedescriptor_t *y,
- int size)
-{
- bool changed = false;
- typecheck_result r;
-
- typedescriptor_t *a = dst;
- typedescriptor_t *b = y;
- while (size--) {
- if (a->type != TYPE_VOID && a->type != b->type) {
- a->type = TYPE_VOID;
- changed = true;
- }
- else if (a->type == TYPE_ADR) {
- if (TYPEINFO_IS_PRIMITIVE(a->typeinfo)) {
- /* 'a' is a returnAddress */
- if (!TYPEINFO_IS_PRIMITIVE(b->typeinfo)
- || (TYPEINFO_RETURNADDRESS(a->typeinfo)
- != TYPEINFO_RETURNADDRESS(b->typeinfo)))
- {
- a->type = TYPE_VOID;
- changed = true;
- }
- }
- else {
- /* 'a' is a reference */
- if (TYPEINFO_IS_PRIMITIVE(b->typeinfo)) {
- a->type = TYPE_VOID;
- changed = true;
- }
- else {
- /* two reference types are merged. There cannot be */
- /* a merge error. In the worst case we get j.l.O. */
- r = typeinfo_merge(m,&(a->typeinfo),&(b->typeinfo));
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
- }
- }
- }
- a++;
- b++;
- }
- return changed;
-}
-
-static typecheck_result typecheck_stackbased_merge(verifier_state *state,
- basicblock *destblock,
- typedescriptor_t *stack,
- s4 stackdepth)
-{
- s4 i;
- s4 destidx;
- typedescriptor_t *stackfloor;
- typedescriptor_t *sp;
- typedescriptor_t *dp;
- typecheck_result r;
- bool changed = false;
-
- destidx = destblock->nr;
-
- if (stackdepth != state->indepth[destidx]) {
- exceptions_throw_verifyerror(state->m, "Stack depth mismatch");
- return typecheck_FAIL;
- }
-
- stackfloor = stack - (stackdepth - 1);
-
- sp = stackfloor;
- dp = state->startstack + (destidx * state->m->maxstack);
-
- for (i=0; i<stackdepth; ++i, ++sp, ++dp) {
- if (sp->type != dp->type) {
- exceptions_throw_verifyerror(state->m, "Mismatched stack types");
- return typecheck_FAIL;
- }
- if (dp->type == TYPE_ADR) {
- if (TYPEINFO_IS_PRIMITIVE(dp->typeinfo)) {
- /* dp has returnAddress type */
- if (TYPEINFO_IS_PRIMITIVE(sp->typeinfo)) {
- if (TYPEINFO_RETURNADDRESS(dp->typeinfo) != TYPEINFO_RETURNADDRESS(sp->typeinfo)) {
- exceptions_throw_verifyerror(state->m, "Mismatched stack types");
- return typecheck_FAIL;
- }
- }
- else {
- exceptions_throw_verifyerror(state->m,"Merging returnAddress with reference");
- return typecheck_FAIL;
- }
- }
- else {
- /* dp has reference type */
- if (TYPEINFO_IS_PRIMITIVE(sp->typeinfo)) {
- exceptions_throw_verifyerror(state->m,"Merging reference with returnAddress");
- return typecheck_FAIL;
- }
- r = typeinfo_merge(state->m,&(dp->typeinfo),&(sp->typeinfo));
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
- }
- }
- }
-
- dp = state->startlocals + (destidx * state->numlocals);
- r = typecheck_stackbased_merge_locals(state->m, dp, state->locals, state->numlocals);
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
-
- return changed;
-}
-
-static bool typecheck_stackbased_reach(verifier_state *state,
- basicblock *destblock,
- typedescriptor_t *stack,
- s4 stackdepth)
-{
- bool changed = false;
- typecheck_result r;
-
- assert(destblock);
-
- if (destblock->flags == BBTYPECHECK_UNDEF) {
- /* The destblock has never been reached before */
-
- TYPECHECK_COUNT(stat_copied);
- LOG1("block L%03d reached first time",destblock->nr); LOGSTR("\t");
- DOLOG( typecheck_stackbased_show_state(state, stack, stack - (stackdepth - 1), false); );
-
- state->indepth[destblock->nr] = stackdepth;
-
- MCOPY(state->startstack + (destblock->nr * state->m->maxstack),
- stack - (stackdepth - 1),
- typedescriptor_t,
- stackdepth);
-
- MCOPY(state->startlocals + (destblock->nr * state->numlocals),
- state->locals,
- typedescriptor_t,
- state->numlocals);
-
- changed = true;
- }
- else {
- /* The destblock has already been reached before */
-
- TYPECHECK_COUNT(stat_merged);
- LOG1("block L%03d reached before", destblock->nr); LOGSTR("\t");
- DOLOG( typecheck_stackbased_show_state(state, stack, stack - (stackdepth - 1), false); );
-
- r = typecheck_stackbased_merge(state, destblock, stack, stackdepth);
- if (r == typecheck_FAIL)
- return false;
- changed = r;
-
- TYPECHECK_COUNTIF(changed,stat_merging_changed);
- }
-
- if (changed) {
- LOG("\tchanged!");
- destblock->flags = BBTYPECHECK_REACHED;
- /* XXX is this check ok? */
- if (destblock->nr <= state->bptr->nr) {
- LOG("\tREPEAT!");
- state->repeat = true;
- }
- }
- return true;
-}
-
-
-/* typecheck_stackbased_verify_fieldaccess *************************************
-
- Verify an ICMD_{GET,PUT}{STATIC,FIELD}
-
- IN:
- state............the current state of the verifier
- instance.........the instance slot, or NULL
- value............the value slot, or NULL
- stack............stack after popping the arguments
-
- RETURN VALUE:
- stack pointer....successful verification,
- NULL.............an exception has been thrown.
-
-*******************************************************************************/
-
-static typedescriptor_t *typecheck_stackbased_verify_fieldaccess(
- verifier_state *state,
- typedescriptor_t *instance,
- typedescriptor_t *value,
- typedescriptor_t *stack)
-{
- jitdata *jd;
-
- jd = state->jd;
-
-#define TYPECHECK_STACKBASED
-#define EXCEPTION do { return NULL; } while (0)
-#define STATE state
-#include <typecheck-fields.inc>
-#undef EXCEPTION
-#undef STATE
-#undef TYPECHECK_STACKBASED
-
- return stack;
-
-throw_stack_overflow:
- LOG("STACK OVERFLOW!");
- exceptions_throw_verifyerror(state->m, "Stack size too large");
- return NULL;
-}
-
-static bool typecheck_stackbased_verify_invocation(verifier_state *state,
- typedescriptor_t *stack,
- typedescriptor_t *stackfloor)
-{
- s4 paramslots;
- methoddesc *md;
- typedescriptor_t *dv;
-
- /* check stack depth */
-
- /* XXX parse params */
-
- INSTRUCTION_GET_METHODDESC(state->iptr, md);
-
- paramslots = md->paramslots;
-
- if ((stack - stackfloor) + 1 < paramslots) {
- exceptions_throw_verifyerror(state->m,
- "Trying to pop operand of an empty stack");
- return false;
- }
-
- dv = stack - (paramslots - 1);
-
-#define TYPECHECK_STACKBASED
-#define OP1 dv
-#include <typecheck-invoke.inc>
-#undef OP1
-#undef TYPECHECK_STACKBASED
-
- return true;
-}
-
-static bool typecheck_stackbased_verify_builtin(verifier_state *state,
- typedescriptor_t *stack,
- typedescriptor_t *stackfloor)
-{
- s4 paramslots;
- typedescriptor_t *dv;
-
- /* check stack depth */
-
- /* XXX parse params */
-
- paramslots = state->iptr->sx.s23.s3.bte->md->paramslots;
-
- if ((stack - stackfloor) + 1 < paramslots) {
- exceptions_throw_verifyerror(state->m,
- "Trying to pop operand of an empty stack");
- return false;
- }
-
- dv = stack - (paramslots - 1);
-
-#define TYPECHECK_STACKBASED
-#define OP1 dv
-#define TYPECHECK_INT(s) CHECK_STACK_TYPE(*(s), TYPE_INT)
-#define TYPECHECK_ADR(s) CHECK_STACK_TYPE(*(s), TYPE_ADR)
-#define TYPECHECK_LNG(s) CHECK_STACK_TYPE(*(s), TYPE_LNG)
-#define TYPECHECK_FLT(s) CHECK_STACK_TYPE(*(s), TYPE_FLT)
-#define TYPECHECK_DBL(s) CHECK_STACK_TYPE(*(s), TYPE_DBL)
-#include <typecheck-builtins.inc>
-#undef OP1
-#undef TYPECHECK_STACKBASED
-
- return true;
-
-throw_stack_type_error:
- exceptions_throw_verifyerror(state->m, "Wrong type on stack"); /* XXX */
- return false;
-}
-
-static bool typecheck_stackbased_multianewarray(verifier_state *state,
- typedescriptor_t *stack,
- typedescriptor_t *stackfloor)
-{
- /* XXX recombine with verify_multianewarray */
-
- classinfo *arrayclass;
- arraydescriptor *desc;
- s4 i;
- typedescriptor_t *sp;
- typedescriptor_t *dst;
-
- /* destination slot */
-
- i = state->iptr->s1.argcount;
-
- dst = stack - (i-1);
-
- /* check the array lengths on the stack */
-
- if ((stack - stackfloor) + 1 < i) {
- exceptions_throw_verifyerror(state->m,
- "Trying to pop operand of an empty stack");
- return false;
- }
-
- if (i < 1)
- TYPECHECK_VERIFYERROR_bool("Illegal dimension argument");
-
- for (sp = dst; sp <= stack; ++sp) {
- if (sp->type != TYPE_INT) {
- exceptions_throw_verifyerror_for_stack(state->m, TYPE_INT);
- return false;
- }
- }
-
- /* check array descriptor */
-
- if (INSTRUCTION_IS_RESOLVED(state->iptr)) {
- /* the array class reference has already been resolved */
- arrayclass = state->iptr->sx.s23.s3.c.cls;
- if (!arrayclass)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with unlinked class");
- if ((desc = arrayclass->vftbl->arraydesc) == NULL)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
- if (desc->dimension < state->iptr->s1.argcount)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
-
- /* set the array type of the result */
- typeinfo_init_classinfo(&(dst->typeinfo), arrayclass);
- }
- else {
- const char *p;
- constant_classref *cr;
-
- /* the array class reference is still unresolved */
- /* check that the reference indicates an array class of correct dimension */
- cr = state->iptr->sx.s23.s3.c.ref;
- i = 0;
- p = cr->name->text;
- while (p[i] == '[')
- i++;
- /* { the dimension of the array class == i } */
- if (i < 1)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
- if (i < state->iptr->s1.argcount)
- TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
-
- /* set the array type of the result */
- if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(cr)))
- return false;
- }
-
- /* everything ok */
- return true;
-
-}
-
-static void typecheck_stackbased_add_jsr_caller(typecheck_jsr_t *jsr,
- basicblock *bptr)
-{
- typecheck_jsr_caller_t *jc;
-
- for (jc = jsr->callers; jc; jc = jc->next)
- if (jc->callblock == bptr)
- return;
-
- jc = DNEW(typecheck_jsr_caller_t);
- jc->next = jsr->callers;
- jc->callblock = bptr;
- jsr->callers = jc;
-}
-
-static typedescriptor_t *typecheck_stackbased_jsr(verifier_state *state,
- typedescriptor_t *stack,
- typedescriptor_t *stackfloor)
-{
- typecheck_jsr_t *jsr;
- basicblock *tbptr;
- jitdata *jd;
- s4 i;
-
- jd = state->jd;
-
- tbptr = state->iptr->sx.s23.s3.jsrtarget.block;
- jsr = state->jsrinfos[tbptr->nr];
-
- if (jsr && tbptr->flags == BBFINISHED) {
-
- LOG1("another JSR to analysed subroutine L%03d", tbptr->nr);
- if (jsr->active) {
- exceptions_throw_verifyerror(state->m, "Recursive JSR");
- return NULL;
- }
-
- assert(jsr->callers);
- assert(jsr->callers->callblock);
-
- /* copy the stack of the RET edge */
-
- MCOPY(stackfloor, jsr->retstack, typedescriptor_t, jsr->retdepth);
- stack = stackfloor + (jsr->retdepth - 1);
-
- /* copy variables that were used in the subroutine from the RET edge */
-
- for (i=0; i<state->numlocals; ++i)
- if (jsr->usedlocals[i])
- state->locals[i] = jsr->retlocals[i];
-
- /* reach the following block */
-
- if (!typecheck_stackbased_reach(state, state->bptr->next, stack,
- (stack - stackfloor) + 1))
- return NULL;
- }
- else {
- if (!jsr) {
- LOG1("first JSR to block L%03d", tbptr->nr);
-
- jsr = DNEW(typecheck_jsr_t);
- state->jsrinfos[tbptr->nr] = jsr;
- jsr->callers = NULL;
- jsr->blockflags = DMNEW(char, state->basicblockcount);
- jsr->retblock = NULL;
- jsr->start = tbptr;
- jsr->usedlocals = DMNEW(char, state->numlocals);
- MZERO(jsr->usedlocals, char, state->numlocals);
- jsr->retlocals = DMNEW(typedescriptor_t, state->numlocals);
- jsr->retstack = DMNEW(typedescriptor_t, state->m->maxstack);
- jsr->retdepth = 0;
- }
- else {
- LOG1("re-analysing JSR to block L%03d", tbptr->nr);
- }
-
- jsr->active = true;
- jsr->next = state->topjsr;
- state->topjsr = jsr;
-
- assert(state->iptr->sx.s23.s3.jsrtarget.block->flags == BBTYPECHECK_REACHED);
-
- tbptr->flags = BBFINISHED;
-
- for (tbptr = state->basicblocks; tbptr != NULL; tbptr = tbptr->next) {
- jsr->blockflags[tbptr->nr] = tbptr->flags;
-
- if (tbptr->flags == BBTYPECHECK_REACHED)
- tbptr->flags = BBFINISHED;
- }
-
- state->iptr->sx.s23.s3.jsrtarget.block->flags = BBTYPECHECK_REACHED;
- }
-
- /* register this block as a caller, if not already done */
-
- typecheck_stackbased_add_jsr_caller(jsr, state->bptr);
-
- return stack;
-}
-
-static bool typecheck_stackbased_ret(verifier_state *state,
- typedescriptor_t *stack,
- typedescriptor_t *stackfloor)
-{
- basicblock *tbptr;
- typecheck_jsr_caller_t *jsrcaller;
- typecheck_jsr_t *jsr;
- s4 i;
-
- /* get the subroutine we are RETurning from */
-
- tbptr = TYPEINFO_RETURNADDRESS(state->locals[state->iptr->s1.varindex].typeinfo);
- if (tbptr == NULL) {
- exceptions_throw_verifyerror(state->m, "Illegal RET");
- return false;
- }
-
- LOG1("RET from subroutine L%03d", tbptr->nr);
- jsr = state->jsrinfos[tbptr->nr];
- assert(jsr);
-
- /* check against recursion */
-
- if (!jsr->active) {
- exceptions_throw_verifyerror(state->m, "RET outside of local subroutine");
- return false;
- }
-
- /* check against multiple RETs for one subroutine */
-
- if (jsr->retblock && jsr->retblock != state->bptr) {
- exceptions_throw_verifyerror(state->m, "Multiple RETs from local subroutine");
- return false;
- }
-
- /* store data-flow of the RET edge */
-
- jsr->retblock = state->bptr;
- jsr->retdepth = (stack - stackfloor) + 1;
- MCOPY(jsr->retstack, stackfloor, typedescriptor_t, jsr->retdepth);
- MCOPY(jsr->retlocals, state->locals, typedescriptor_t, state->numlocals);
-
- /* invalidate the returnAddress used by this RET */
- /* XXX should we also invalidate the returnAddresses of JSRs that are skipped by this RET? */
-
- for (i=0; i<state->numlocals; ++i) {
- typedescriptor_t *lc = &(jsr->retlocals[i]);
- if (TYPE_IS_RETURNADDRESS(lc->type, lc->typeinfo))
- if (TYPEINFO_RETURNADDRESS(lc->typeinfo) == tbptr) {
- LOG1("invalidating returnAddress in local %d", i);
- TYPEINFO_INIT_RETURNADDRESS(lc->typeinfo, NULL);
- }
- }
-
- /* touch all callers of the subroutine, so they are analysed again */
-
- for (jsrcaller = jsr->callers; jsrcaller != NULL; jsrcaller = jsrcaller->next) {
- tbptr = jsrcaller->callblock;
- LOG1("touching caller L%03d from RET", tbptr->nr);
- assert(jsr->blockflags[tbptr->nr] >= BBFINISHED);
- jsr->blockflags[tbptr->nr] = BBTYPECHECK_REACHED; /* XXX repeat? */
- }
-
- return true;
-}
-
-bool typecheck_stackbased(jitdata *jd)
-{
- register verifier_slot_t *stack;
- verifier_slot_t *stackfloor;
- s4 len;
- methoddesc *md;
- bool maythrow;
- bool superblockend;
- verifier_slot_t temp;
- branch_target_t *table;
- lookup_target_t *lookup;
- s4 i;
- typecheck_result r;
- verifier_slot_t *dst;
- verifier_state state;
- basicblock *tbptr;
- exception_entry *ex;
- typedescriptor_t exstack;
- s4 skip = 0;
-
- DOLOG( show_method(jd, SHOW_PARSE); );
-
- /* initialize verifier state */
-
- state.jd = jd;
- state.m = jd->m;
- state.cd = jd->cd;
- state.basicblocks = jd->basicblocks;
- state.basicblockcount = jd->basicblockcount;
- state.topjsr = NULL;
-# define STATE (&state)
-
- /* check that the basicblock numbers are valid */
-
-#if !defined(NDEBUG)
- jit_check_basicblock_numbers(jd);
-#endif
-
- /* check if this method is an instance initializer method */
-
- state.initmethod = (state.m->name == utf_init);
-
- /* allocate parameter descriptors if necessary */
-
- if (!state.m->parseddesc->params)
- if (!descriptor_params_from_paramtypes(state.m->parseddesc,state.m->flags))
- return false;
-
- /* allocate the stack buffers */
-
- stackfloor = DMNEW(verifier_slot_t, state.m->maxstack + 1);
- state.stackceiling = stackfloor + state.m->maxstack;
- stack = stackfloor - 1;
- state.indepth = DMNEW(s4, state.basicblockcount);
- state.startstack = DMNEW(verifier_slot_t, state.m->maxstack * state.basicblockcount);
-
- /* allocate the local variables buffers */
-
- state.numlocals = state.m->maxlocals;
- state.validlocals = state.m->maxlocals;
- if (state.initmethod)
- state.numlocals++; /* extra marker variable */
-
- state.locals = DMNEW(verifier_slot_t, state.numlocals);
- state.startlocals = DMNEW(verifier_slot_t, state.numlocals * state.basicblockcount);
-
- /* allocate the buffer of active exception handlers */
-
- state.handlers = DMNEW(exception_entry*, state.jd->exceptiontablelength + 1);
-
- /* initialize instack of exception handlers */
-
- exstack.type = TYPE_ADR;
- typeinfo_init_classinfo(&(exstack.typeinfo),
- class_java_lang_Throwable); /* changed later */
-
- LOG("Exception handler stacks set.\n");
-
- /* initialize jsr info buffer */
-
- state.jsrinfos = DMNEW(typecheck_jsr_t *, state.basicblockcount);
- MZERO(state.jsrinfos, typecheck_jsr_t *, state.basicblockcount);
-
- /* initialize stack of first block */
-
- state.indepth[0] = 0;
-
- /* initialize locals of first block */
-
- /* if this is an instance method initialize the "this" ref type */
-
- if (!(state.m->flags & ACC_STATIC)) {
- if (state.validlocals < 1)
- VERIFY_ERROR("Not enough local variables for method arguments");
- dst = state.startlocals;
- dst->type = TYPE_ADR;
- if (state.initmethod)
- TYPEINFO_INIT_NEWOBJECT(dst->typeinfo, NULL);
- else
- typeinfo_init_classinfo(&(dst->typeinfo), state.m->clazz);
-
- skip = 1;
- }
-
- LOG("'this' argument set.\n");
-
- len = typedescriptors_init_from_methoddesc(state.startlocals + skip,
- state.m->parseddesc,
- state.validlocals, true, skip, &state.returntype);
- if (len < 0)
- return false;
-
- /* set remaining locals to void */
-
- for (i = skip + len; i<state.numlocals; ++i)
- state.startlocals[i].type = TYPE_VOID;
-
- /* initialize block flags */
-
- typecheck_init_flags(&state, BBUNDEF);
-
- /* iterate until fixpoint reached */
-
- do {
-
- state.repeat = false;
-
- /* iterate over the basic blocks */
-
- for (state.bptr = state.basicblocks; state.bptr != NULL; state.bptr = state.bptr->next) {
-
- if (state.bptr->flags != BBTYPECHECK_REACHED)
- continue;
-
- DOLOG( show_basicblock(jd, state.bptr, SHOW_PARSE); );
-
- /* mark this block as analysed */
-
- state.bptr->flags = BBFINISHED;
-
- /* determine the active exception handlers for this block */
- /* XXX could use a faster algorithm with sorted lists or */
- /* something? */
- /* XXX reuse code from variables based verifer? */
- len = 0;
- for (ex = STATE->jd->exceptiontable; ex ; ex = ex->down) {
- if ((ex->start->nr <= STATE->bptr->nr) && (ex->end->nr > STATE->bptr->nr)) {
- LOG1("\tactive handler L%03d", ex->handler->nr);
- STATE->handlers[len++] = ex;
- }
- }
- STATE->handlers[len] = NULL;
-
- /* initialize the locals */
-
- MCOPY(state.locals,
- state.startlocals + (state.bptr->nr * state.numlocals),
- verifier_slot_t, state.numlocals);
-
- /* initialize the stack */
-
- len = state.indepth[state.bptr->nr];
-
- MCOPY(stackfloor,
- state.startstack + (state.bptr->nr * state.m->maxstack),
- verifier_slot_t, len);
-
- stack = stackfloor + (len - 1);
-
- /* iterate over the instructions in this block */
-
- state.iptr = state.bptr->iinstr;
- len = state.bptr->icount;
-
- superblockend = false;
-
- for (; len--; state.iptr++) {
-
- maythrow = false;
-
- LOGNL;
- DOLOG( typecheck_stackbased_show_state(&state, stack, stackfloor, true); );
-
- switch (state.iptr->opc) {
-#define TYPECHECK_STACKBASED 1
-#define STATE (&state)
-#define IPTR state.iptr
-#define BPTR state.bptr
-#define METHOD state.m
-#define LOCAL_SLOT(index) (state.locals + (index))
-#define EXCEPTION \
- do { \
- LOG("EXCEPTION THROWN!\n"); \
- return false; \
- } while (0)
-
-#include <typecheck-stackbased-gen.inc>
-#undef TYPECHECK_STACKBASED
- }
-
- /* reach exception handlers for this instruction */
-
- if (maythrow) {
- TYPECHECK_COUNT(stat_ins_maythrow);
- TYPECHECK_MARK(STATE->stat_maythrow);
- LOG("\treaching exception handlers");
- i = 0;
- while (STATE->handlers[i]) {
- TYPECHECK_COUNT(stat_handlers_reached);
- if (STATE->handlers[i]->catchtype.any)
- exstack.typeinfo.typeclass = STATE->handlers[i]->catchtype;
- else
- exstack.typeinfo.typeclass.cls = class_java_lang_Throwable;
- if (!typecheck_stackbased_reach(
- STATE,
- STATE->handlers[i]->handler,
- &exstack, 1))
- EXCEPTION;
- i++;
- }
- }
- }
-
- /* propagate types to the following block */
-
- if (!superblockend) {
- if (!typecheck_stackbased_reach(&state, state.bptr->next,
- stack, stack - stackfloor + 1))
- EXCEPTION;
- }
- } /* end loop over blocks */
-
- while (!state.repeat && state.topjsr) {
- LOG1("done analysing subroutine L%03d", state.topjsr->start->nr);
-
- /* propagate down used locals */
-
- if (state.topjsr->next) {
- for (i=0; i<state.numlocals; ++i)
- state.topjsr->next->usedlocals[i] |= state.topjsr->usedlocals[i];
- }
-
- /* restore REACHED flags */
-
- for (tbptr = state.basicblocks; tbptr != NULL; tbptr = tbptr->next) {
- assert(tbptr->flags != BBTYPECHECK_REACHED);
- if (state.topjsr->blockflags[tbptr->nr] == BBTYPECHECK_REACHED) {
- tbptr->flags = BBTYPECHECK_REACHED;
- state.repeat = true;
- }
- }
-
- /* dactivate the subroutine */
-
- state.topjsr->active = false;
- state.topjsr = state.topjsr->next;
- }
- } while (state.repeat);
-
- /* reset block flags */
-
- typecheck_reset_flags(&state);
-
- LOG("typecheck_stackbased successful");
-
- return true;
-
-throw_stack_underflow:
- LOG("STACK UNDERFLOW!");
- exceptions_throw_verifyerror(state.m, "Unable to pop operand off an empty stack");
- return false;
-
-throw_stack_overflow:
- LOG("STACK OVERFLOW!");
- exceptions_throw_verifyerror(state.m, "Stack size too large");
- return false;
-
-throw_stack_type_error:
- LOG("STACK TYPE ERROR!");
- exceptions_throw_verifyerror(state.m, "Mismatched stack types");
- return false;
-
-throw_local_type_error:
- LOG("LOCAL TYPE ERROR!");
- exceptions_throw_verifyerror(state.m, "Local variable type mismatch");
- return false;
-
-throw_stack_category_error:
- LOG("STACK CATEGORY ERROR!");
- exceptions_throw_verifyerror(state.m, "Attempt to split long or double on the stack");
- return false;
-}
-
-
-#if defined(TYPECHECK_VERBOSE)
-static void typecheck_stackbased_show_state(verifier_state *state,
- typedescriptor_t *stack,
- typedescriptor_t *stackfloor,
- bool showins)
-{
- typedescriptor_t *sp;
- s4 i;
-
- LOGSTR1("stackdepth %d stack [", (stack - stackfloor) + 1);
- for (sp=stackfloor; sp <= stack; sp++) {
- LOGSTR(" ");
- DOLOG( typedescriptor_print(stdout, sp); );
- }
- LOGSTR(" ] locals [");
- for (i=0; i<state->numlocals; ++i) {
- LOGSTR(" ");
- DOLOG( typedescriptor_print(stdout, state->locals + i); );
- }
- LOGSTR(" ]");
- LOGNL;
- if (showins && state->iptr < (state->bptr->iinstr + state->bptr->icount)) {
- LOGSTR("\t");
- DOLOG( show_icmd(state->jd, state->iptr, false, SHOW_PARSE); );
- LOGNL;
- }
-}
-#endif
-
-#endif /* defined(ENABLE_VERIFIER) */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
-
--- /dev/null
+/* src/vm/jit/verify/typecheck-stackbased.c - stack-based verifier
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+
+#include "vm/types.h"
+
+#include "vm/jit/builtin.hpp"
+#include "mm/memory.h"
+
+#include "vm/array.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/global.h"
+#include "vm/globals.hpp"
+#include "vm/primitive.hpp"
+
+#include "vm/jit/parse.hpp"
+#include "vm/jit/show.hpp"
+#include "vm/jit/stack.h"
+#include "vm/jit/verify/typecheck-common.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* this #if runs over the whole file: */
+#if defined(ENABLE_VERIFIER)
+
+typedef typedescriptor_t verifier_slot_t;
+
+#if defined(TYPECHECK_VERBOSE)
+static void typecheck_stackbased_show_state(verifier_state *state,
+ typedescriptor *stack,
+ typedescriptor *stackfloor,
+ bool showins);
+#endif
+
+
+#define CHECK_STACK_DEPTH(d) \
+ if (((u1*)stack - (u1*)stackfloor) \
+ < (((d)-1) * (int)sizeof(verifier_slot_t))) \
+ goto throw_stack_underflow;
+
+/* XXX don't need to check against ACONST for every ICMD */
+#define CHECK_STACK_SPACE(d) \
+ if (((u1*)STATE->stackceiling - (u1*)stack) \
+ < (((d)+1) * (int)sizeof(verifier_slot_t))) \
+ if (STATE->iptr->opc != ICMD_ACONST \
+ || INSTRUCTION_MUST_CHECK(STATE->iptr)) \
+ goto throw_stack_overflow;
+
+#define CHECK_STACK_TYPE(s, t) \
+ if ((s).type != (t)) \
+ goto throw_stack_type_error;
+
+/* XXX inefficient */
+#define CHECK_LOCAL_TYPE(index, t) \
+ do { \
+ if (state.locals[(index)].type != (t)) \
+ goto throw_local_type_error; \
+ if (STATE->topjsr) \
+ STATE->topjsr->usedlocals[(index)] = 1; \
+ if (STATE->topjsr && IS_2_WORD_TYPE(t)) \
+ STATE->topjsr->usedlocals[(index) + 1] = 1; \
+ } while(0)
+
+/* XXX inefficient */
+#define STORE_LOCAL(t, index) \
+ do { \
+ state.locals[(index)].type = (t); \
+ if ((index) && IS_2_WORD_TYPE(state.locals[(index)-1].type)) \
+ state.locals[(index-1)].type = TYPE_VOID; \
+ if (STATE->topjsr) \
+ STATE->topjsr->usedlocals[(index)] = 1; \
+ } while (0)
+
+/* XXX inefficient */
+#define STORE_LOCAL_2_WORD(t, index) \
+ do { \
+ STORE_LOCAL(t, index); \
+ state.locals[(index)+1].type = TYPE_VOID; \
+ if (STATE->topjsr) \
+ STATE->topjsr->usedlocals[(index)] = 1; \
+ } while (0)
+
+#define VERIFY_ERROR(msg) \
+ do { \
+ LOG1("VerifyError: %s", msg); \
+ exceptions_throw_verifyerror(STATE->m, msg); \
+ return false; \
+ } while (0)
+
+#define IS_CAT1(slot) \
+ ((slot).type != TYPE_VOID && !IS_2_WORD_TYPE((slot).type))
+
+#define IS_CAT2(slot) \
+ ((slot).type != TYPE_VOID && IS_2_WORD_TYPE((slot).type))
+
+#define CHECK_CAT1(slot) \
+ do { \
+ if (!IS_CAT1(slot)) \
+ goto throw_stack_category_error; \
+ } while (0)
+
+#define CHECK_CAT2(slot) \
+ do { \
+ if (!IS_CAT2(slot)) \
+ goto throw_stack_category_error; \
+ } while (0)
+
+#define COPY_SLOT(s, d) \
+ do { (d) = (s); } while (0)
+
+#define REACH_BLOCK(target) \
+ do { \
+ if (!typecheck_stackbased_reach(STATE, (target), stack, \
+ (stack - stackfloor) + 1)) \
+ return false; \
+ } while (0)
+
+#define REACH(target) \
+ do { \
+ REACH_BLOCK((target).block); \
+ } while (0)
+
+#undef TYPECHECK_INT
+#undef TYPECHECK_LNG
+#undef TYPECHECK_FLT
+#undef TYPECHECK_DBL
+#undef TYPECHECK_ADR
+
+/* XXX should reuse typevector code */
+static typecheck_result typecheck_stackbased_merge_locals(methodinfo *m,
+ typedescriptor_t *dst,
+ typedescriptor_t *y,
+ int size)
+{
+ bool changed = false;
+ typecheck_result r;
+
+ typedescriptor_t *a = dst;
+ typedescriptor_t *b = y;
+ while (size--) {
+ if (a->type != TYPE_VOID && a->type != b->type) {
+ a->type = TYPE_VOID;
+ changed = true;
+ }
+ else if (a->type == TYPE_ADR) {
+ if (TYPEINFO_IS_PRIMITIVE(a->typeinfo)) {
+ /* 'a' is a returnAddress */
+ if (!TYPEINFO_IS_PRIMITIVE(b->typeinfo)
+ || (TYPEINFO_RETURNADDRESS(a->typeinfo)
+ != TYPEINFO_RETURNADDRESS(b->typeinfo)))
+ {
+ a->type = TYPE_VOID;
+ changed = true;
+ }
+ }
+ else {
+ /* 'a' is a reference */
+ if (TYPEINFO_IS_PRIMITIVE(b->typeinfo)) {
+ a->type = TYPE_VOID;
+ changed = true;
+ }
+ else {
+ /* two reference types are merged. There cannot be */
+ /* a merge error. In the worst case we get j.l.O. */
+ r = typeinfo_merge(m,&(a->typeinfo),&(b->typeinfo));
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+ }
+ }
+ }
+ a++;
+ b++;
+ }
+ return (typecheck_result) changed;
+}
+
+static typecheck_result typecheck_stackbased_merge(verifier_state *state,
+ basicblock *destblock,
+ typedescriptor_t *stack,
+ s4 stackdepth)
+{
+ s4 i;
+ s4 destidx;
+ typedescriptor_t *stackfloor;
+ typedescriptor_t *sp;
+ typedescriptor_t *dp;
+ typecheck_result r;
+ bool changed = false;
+
+ destidx = destblock->nr;
+
+ if (stackdepth != state->indepth[destidx]) {
+ exceptions_throw_verifyerror(state->m, "Stack depth mismatch");
+ return typecheck_FAIL;
+ }
+
+ stackfloor = stack - (stackdepth - 1);
+
+ sp = stackfloor;
+ dp = state->startstack + (destidx * state->m->maxstack);
+
+ for (i=0; i<stackdepth; ++i, ++sp, ++dp) {
+ if (sp->type != dp->type) {
+ exceptions_throw_verifyerror(state->m, "Mismatched stack types");
+ return typecheck_FAIL;
+ }
+ if (dp->type == TYPE_ADR) {
+ if (TYPEINFO_IS_PRIMITIVE(dp->typeinfo)) {
+ /* dp has returnAddress type */
+ if (TYPEINFO_IS_PRIMITIVE(sp->typeinfo)) {
+ if (TYPEINFO_RETURNADDRESS(dp->typeinfo) != TYPEINFO_RETURNADDRESS(sp->typeinfo)) {
+ exceptions_throw_verifyerror(state->m, "Mismatched stack types");
+ return typecheck_FAIL;
+ }
+ }
+ else {
+ exceptions_throw_verifyerror(state->m,"Merging returnAddress with reference");
+ return typecheck_FAIL;
+ }
+ }
+ else {
+ /* dp has reference type */
+ if (TYPEINFO_IS_PRIMITIVE(sp->typeinfo)) {
+ exceptions_throw_verifyerror(state->m,"Merging reference with returnAddress");
+ return typecheck_FAIL;
+ }
+ r = typeinfo_merge(state->m,&(dp->typeinfo),&(sp->typeinfo));
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+ }
+ }
+ }
+
+ dp = state->startlocals + (destidx * state->numlocals);
+ r = typecheck_stackbased_merge_locals(state->m, dp, state->locals, state->numlocals);
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+
+ return (typecheck_result) changed;
+}
+
+static bool typecheck_stackbased_reach(verifier_state *state,
+ basicblock *destblock,
+ typedescriptor_t *stack,
+ s4 stackdepth)
+{
+ bool changed = false;
+ typecheck_result r;
+
+ assert(destblock);
+
+ if (destblock->flags == BBTYPECHECK_UNDEF) {
+ /* The destblock has never been reached before */
+
+ TYPECHECK_COUNT(stat_copied);
+ LOG1("block L%03d reached first time",destblock->nr); LOGSTR("\t");
+ DOLOG( typecheck_stackbased_show_state(state, stack, stack - (stackdepth - 1), false); );
+
+ state->indepth[destblock->nr] = stackdepth;
+
+ MCOPY(state->startstack + (destblock->nr * state->m->maxstack),
+ stack - (stackdepth - 1),
+ typedescriptor_t,
+ stackdepth);
+
+ MCOPY(state->startlocals + (destblock->nr * state->numlocals),
+ state->locals,
+ typedescriptor_t,
+ state->numlocals);
+
+ changed = true;
+ }
+ else {
+ /* The destblock has already been reached before */
+
+ TYPECHECK_COUNT(stat_merged);
+ LOG1("block L%03d reached before", destblock->nr); LOGSTR("\t");
+ DOLOG( typecheck_stackbased_show_state(state, stack, stack - (stackdepth - 1), false); );
+
+ r = typecheck_stackbased_merge(state, destblock, stack, stackdepth);
+ if (r == typecheck_FAIL)
+ return false;
+ changed = r;
+
+ TYPECHECK_COUNTIF(changed,stat_merging_changed);
+ }
+
+ if (changed) {
+ LOG("\tchanged!");
+ destblock->flags = BBTYPECHECK_REACHED;
+ /* XXX is this check ok? */
+ if (destblock->nr <= state->bptr->nr) {
+ LOG("\tREPEAT!");
+ state->repeat = true;
+ }
+ }
+ return true;
+}
+
+
+/* typecheck_stackbased_verify_fieldaccess *************************************
+
+ Verify an ICMD_{GET,PUT}{STATIC,FIELD}
+
+ IN:
+ state............the current state of the verifier
+ instance.........the instance slot, or NULL
+ value............the value slot, or NULL
+ stack............stack after popping the arguments
+
+ RETURN VALUE:
+ stack pointer....successful verification,
+ NULL.............an exception has been thrown.
+
+*******************************************************************************/
+
+static typedescriptor_t *typecheck_stackbased_verify_fieldaccess(
+ verifier_state *state,
+ typedescriptor_t *instance,
+ typedescriptor_t *value,
+ typedescriptor_t *stack)
+{
+ jitdata *jd;
+
+ jd = state->jd;
+
+#define TYPECHECK_STACKBASED
+#define EXCEPTION do { return NULL; } while (0)
+#define STATE state
+#include <typecheck-fields.inc>
+#undef EXCEPTION
+#undef STATE
+#undef TYPECHECK_STACKBASED
+
+ return stack;
+
+throw_stack_overflow:
+ LOG("STACK OVERFLOW!");
+ exceptions_throw_verifyerror(state->m, "Stack size too large");
+ return NULL;
+}
+
+static bool typecheck_stackbased_verify_invocation(verifier_state *state,
+ typedescriptor_t *stack,
+ typedescriptor_t *stackfloor)
+{
+ s4 paramslots;
+ methoddesc *md;
+ typedescriptor_t *dv;
+
+ /* check stack depth */
+
+ /* XXX parse params */
+
+ INSTRUCTION_GET_METHODDESC(state->iptr, md);
+
+ paramslots = md->paramslots;
+
+ if ((stack - stackfloor) + 1 < paramslots) {
+ exceptions_throw_verifyerror(state->m,
+ "Trying to pop operand of an empty stack");
+ return false;
+ }
+
+ dv = stack - (paramslots - 1);
+
+#define TYPECHECK_STACKBASED
+#define OP1 dv
+#include <typecheck-invoke.inc>
+#undef OP1
+#undef TYPECHECK_STACKBASED
+
+ return true;
+}
+
+static bool typecheck_stackbased_verify_builtin(verifier_state *state,
+ typedescriptor_t *stack,
+ typedescriptor_t *stackfloor)
+{
+ s4 paramslots;
+ typedescriptor_t *dv;
+
+ /* check stack depth */
+
+ /* XXX parse params */
+
+ paramslots = state->iptr->sx.s23.s3.bte->md->paramslots;
+
+ if ((stack - stackfloor) + 1 < paramslots) {
+ exceptions_throw_verifyerror(state->m,
+ "Trying to pop operand of an empty stack");
+ return false;
+ }
+
+ dv = stack - (paramslots - 1);
+
+#define TYPECHECK_STACKBASED
+#define OP1 dv
+#define TYPECHECK_INT(s) CHECK_STACK_TYPE(*(s), TYPE_INT)
+#define TYPECHECK_ADR(s) CHECK_STACK_TYPE(*(s), TYPE_ADR)
+#define TYPECHECK_LNG(s) CHECK_STACK_TYPE(*(s), TYPE_LNG)
+#define TYPECHECK_FLT(s) CHECK_STACK_TYPE(*(s), TYPE_FLT)
+#define TYPECHECK_DBL(s) CHECK_STACK_TYPE(*(s), TYPE_DBL)
+#include <typecheck-builtins.inc>
+#undef OP1
+#undef TYPECHECK_STACKBASED
+
+ return true;
+
+throw_stack_type_error:
+ exceptions_throw_verifyerror(state->m, "Wrong type on stack"); /* XXX */
+ return false;
+}
+
+static bool typecheck_stackbased_multianewarray(verifier_state *state,
+ typedescriptor_t *stack,
+ typedescriptor_t *stackfloor)
+{
+ /* XXX recombine with verify_multianewarray */
+
+ classinfo *arrayclass;
+ arraydescriptor *desc;
+ s4 i;
+ typedescriptor_t *sp;
+ typedescriptor_t *dst;
+
+ /* destination slot */
+
+ i = state->iptr->s1.argcount;
+
+ dst = stack - (i-1);
+
+ /* check the array lengths on the stack */
+
+ if ((stack - stackfloor) + 1 < i) {
+ exceptions_throw_verifyerror(state->m,
+ "Trying to pop operand of an empty stack");
+ return false;
+ }
+
+ if (i < 1)
+ TYPECHECK_VERIFYERROR_bool("Illegal dimension argument");
+
+ for (sp = dst; sp <= stack; ++sp) {
+ if (sp->type != TYPE_INT) {
+ exceptions_throw_verifyerror_for_stack(state->m, TYPE_INT);
+ return false;
+ }
+ }
+
+ /* check array descriptor */
+
+ if (INSTRUCTION_IS_RESOLVED(state->iptr)) {
+ /* the array class reference has already been resolved */
+ arrayclass = state->iptr->sx.s23.s3.c.cls;
+ if (!arrayclass)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with unlinked class");
+ if ((desc = arrayclass->vftbl->arraydesc) == NULL)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
+ if (desc->dimension < state->iptr->s1.argcount)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
+
+ /* set the array type of the result */
+ typeinfo_init_classinfo(&(dst->typeinfo), arrayclass);
+ }
+ else {
+ const char *p;
+ constant_classref *cr;
+
+ /* the array class reference is still unresolved */
+ /* check that the reference indicates an array class of correct dimension */
+ cr = state->iptr->sx.s23.s3.c.ref;
+ i = 0;
+ p = cr->name->text;
+ while (p[i] == '[')
+ i++;
+ /* { the dimension of the array class == i } */
+ if (i < 1)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY with non-array class");
+ if (i < state->iptr->s1.argcount)
+ TYPECHECK_VERIFYERROR_bool("MULTIANEWARRAY dimension to high");
+
+ /* set the array type of the result */
+ if (!typeinfo_init_class(&(dst->typeinfo),CLASSREF_OR_CLASSINFO(cr)))
+ return false;
+ }
+
+ /* everything ok */
+ return true;
+
+}
+
+static void typecheck_stackbased_add_jsr_caller(typecheck_jsr_t *jsr,
+ basicblock *bptr)
+{
+ typecheck_jsr_caller_t *jc;
+
+ for (jc = jsr->callers; jc; jc = jc->next)
+ if (jc->callblock == bptr)
+ return;
+
+ jc = (typecheck_jsr_caller_t*) DumpMemory::allocate(sizeof(typecheck_jsr_caller_t));
+ jc->next = jsr->callers;
+ jc->callblock = bptr;
+ jsr->callers = jc;
+}
+
+static typedescriptor_t *typecheck_stackbased_jsr(verifier_state *state,
+ typedescriptor_t *stack,
+ typedescriptor_t *stackfloor)
+{
+ typecheck_jsr_t *jsr;
+ basicblock *tbptr;
+ jitdata *jd;
+ s4 i;
+
+ jd = state->jd;
+
+ tbptr = state->iptr->sx.s23.s3.jsrtarget.block;
+ jsr = state->jsrinfos[tbptr->nr];
+
+ if (jsr && tbptr->flags == BBFINISHED) {
+
+ LOG1("another JSR to analysed subroutine L%03d", tbptr->nr);
+ if (jsr->active) {
+ exceptions_throw_verifyerror(state->m, "Recursive JSR");
+ return NULL;
+ }
+
+ assert(jsr->callers);
+ assert(jsr->callers->callblock);
+
+ /* copy the stack of the RET edge */
+
+ MCOPY(stackfloor, jsr->retstack, typedescriptor_t, jsr->retdepth);
+ stack = stackfloor + (jsr->retdepth - 1);
+
+ /* copy variables that were used in the subroutine from the RET edge */
+
+ for (i=0; i<state->numlocals; ++i)
+ if (jsr->usedlocals[i])
+ state->locals[i] = jsr->retlocals[i];
+
+ /* reach the following block */
+
+ if (!typecheck_stackbased_reach(state, state->bptr->next, stack,
+ (stack - stackfloor) + 1))
+ return NULL;
+ }
+ else {
+ if (!jsr) {
+ LOG1("first JSR to block L%03d", tbptr->nr);
+
+ jsr = (typecheck_jsr_t*) DumpMemory::allocate(sizeof(typecheck_jsr_t));
+ state->jsrinfos[tbptr->nr] = jsr;
+ jsr->callers = NULL;
+ jsr->blockflags = (char*) DumpMemory::allocate(sizeof(char) * state->basicblockcount);
+ jsr->retblock = NULL;
+ jsr->start = tbptr;
+ jsr->usedlocals = (char*) DumpMemory::allocate(sizeof(char) * state->numlocals);
+ MZERO(jsr->usedlocals, char, state->numlocals);
+ jsr->retlocals = (typedescriptor_t*) DumpMemory::allocate(sizeof(typedescriptor_t) * state->numlocals);
+ jsr->retstack = (typedescriptor_t*) DumpMemory::allocate(sizeof(typedescriptor_t) * state->m->maxstack);
+ jsr->retdepth = 0;
+ }
+ else {
+ LOG1("re-analysing JSR to block L%03d", tbptr->nr);
+ }
+
+ jsr->active = true;
+ jsr->next = state->topjsr;
+ state->topjsr = jsr;
+
+ assert(state->iptr->sx.s23.s3.jsrtarget.block->flags == BBTYPECHECK_REACHED);
+
+ tbptr->flags = BBFINISHED;
+
+ for (tbptr = state->basicblocks; tbptr != NULL; tbptr = tbptr->next) {
+ jsr->blockflags[tbptr->nr] = tbptr->flags;
+
+ if (tbptr->flags == BBTYPECHECK_REACHED)
+ tbptr->flags = BBFINISHED;
+ }
+
+ state->iptr->sx.s23.s3.jsrtarget.block->flags = BBTYPECHECK_REACHED;
+ }
+
+ /* register this block as a caller, if not already done */
+
+ typecheck_stackbased_add_jsr_caller(jsr, state->bptr);
+
+ return stack;
+}
+
+static bool typecheck_stackbased_ret(verifier_state *state,
+ typedescriptor_t *stack,
+ typedescriptor_t *stackfloor)
+{
+ basicblock *tbptr;
+ typecheck_jsr_caller_t *jsrcaller;
+ typecheck_jsr_t *jsr;
+ s4 i;
+
+ /* get the subroutine we are RETurning from */
+
+ tbptr = (basicblock*) TYPEINFO_RETURNADDRESS(state->locals[state->iptr->s1.varindex].typeinfo);
+ if (tbptr == NULL) {
+ exceptions_throw_verifyerror(state->m, "Illegal RET");
+ return false;
+ }
+
+ LOG1("RET from subroutine L%03d", tbptr->nr);
+ jsr = state->jsrinfos[tbptr->nr];
+ assert(jsr);
+
+ /* check against recursion */
+
+ if (!jsr->active) {
+ exceptions_throw_verifyerror(state->m, "RET outside of local subroutine");
+ return false;
+ }
+
+ /* check against multiple RETs for one subroutine */
+
+ if (jsr->retblock && jsr->retblock != state->bptr) {
+ exceptions_throw_verifyerror(state->m, "Multiple RETs from local subroutine");
+ return false;
+ }
+
+ /* store data-flow of the RET edge */
+
+ jsr->retblock = state->bptr;
+ jsr->retdepth = (stack - stackfloor) + 1;
+ MCOPY(jsr->retstack, stackfloor, typedescriptor_t, jsr->retdepth);
+ MCOPY(jsr->retlocals, state->locals, typedescriptor_t, state->numlocals);
+
+ /* invalidate the returnAddress used by this RET */
+ /* XXX should we also invalidate the returnAddresses of JSRs that are skipped by this RET? */
+
+ for (i=0; i<state->numlocals; ++i) {
+ typedescriptor_t *lc = &(jsr->retlocals[i]);
+ if (TYPE_IS_RETURNADDRESS(lc->type, lc->typeinfo))
+ if (TYPEINFO_RETURNADDRESS(lc->typeinfo) == tbptr) {
+ LOG1("invalidating returnAddress in local %d", i);
+ TYPEINFO_INIT_RETURNADDRESS(lc->typeinfo, NULL);
+ }
+ }
+
+ /* touch all callers of the subroutine, so they are analysed again */
+
+ for (jsrcaller = jsr->callers; jsrcaller != NULL; jsrcaller = jsrcaller->next) {
+ tbptr = jsrcaller->callblock;
+ LOG1("touching caller L%03d from RET", tbptr->nr);
+ assert(jsr->blockflags[tbptr->nr] >= BBFINISHED);
+ jsr->blockflags[tbptr->nr] = BBTYPECHECK_REACHED; /* XXX repeat? */
+ }
+
+ return true;
+}
+
+bool typecheck_stackbased(jitdata *jd)
+{
+ register verifier_slot_t *stack;
+ verifier_slot_t *stackfloor;
+ s4 len;
+ methoddesc *md;
+ bool maythrow;
+ bool superblockend;
+ verifier_slot_t temp;
+ branch_target_t *table;
+ lookup_target_t *lookup;
+ s4 i;
+ typecheck_result r;
+ verifier_slot_t *dst;
+ verifier_state state;
+ basicblock *tbptr;
+ exception_entry *ex;
+ typedescriptor_t exstack;
+ s4 skip = 0;
+
+ DOLOG( show_method(jd, SHOW_PARSE); );
+
+ /* initialize verifier state */
+
+ state.jd = jd;
+ state.m = jd->m;
+ state.cd = jd->cd;
+ state.basicblocks = jd->basicblocks;
+ state.basicblockcount = jd->basicblockcount;
+ state.topjsr = NULL;
+# define STATE (&state)
+
+ /* check that the basicblock numbers are valid */
+
+#if !defined(NDEBUG)
+ jit_check_basicblock_numbers(jd);
+#endif
+
+ /* check if this method is an instance initializer method */
+
+ state.initmethod = (state.m->name == utf_init);
+
+ /* allocate parameter descriptors if necessary */
+
+ if (!state.m->parseddesc->params)
+ if (!descriptor_params_from_paramtypes(state.m->parseddesc,state.m->flags))
+ return false;
+
+ /* allocate the stack buffers */
+
+ stackfloor = (verifier_slot_t*) DumpMemory::allocate(sizeof(verifier_slot_t) * (state.m->maxstack + 1));
+ state.stackceiling = stackfloor + state.m->maxstack;
+ stack = stackfloor - 1;
+ state.indepth = (s4*) DumpMemory::allocate(sizeof(s4) * state.basicblockcount);
+ state.startstack = (verifier_slot_t*) DumpMemory::allocate(sizeof(verifier_slot_t) * state.m->maxstack * state.basicblockcount);
+
+ /* allocate the local variables buffers */
+
+ state.numlocals = state.m->maxlocals;
+ state.validlocals = state.m->maxlocals;
+ if (state.initmethod)
+ state.numlocals++; /* extra marker variable */
+
+ state.locals = (verifier_slot_t*) DumpMemory::allocate(sizeof(verifier_slot_t) * state.numlocals);
+ state.startlocals = (verifier_slot_t*) DumpMemory::allocate(sizeof(verifier_slot_t) * state.numlocals * state.basicblockcount);
+
+ /* allocate the buffer of active exception handlers */
+
+ state.handlers = (exception_entry**) DumpMemory::allocate(sizeof(exception_entry*) * (state.jd->exceptiontablelength + 1));
+
+ /* initialize instack of exception handlers */
+
+ exstack.type = TYPE_ADR;
+ typeinfo_init_classinfo(&(exstack.typeinfo),
+ class_java_lang_Throwable); /* changed later */
+
+ LOG("Exception handler stacks set.\n");
+
+ /* initialize jsr info buffer */
+
+ state.jsrinfos = (typecheck_jsr_t**) DumpMemory::allocate(sizeof(typecheck_jsr_t*) * state.basicblockcount);
+ MZERO(state.jsrinfos, typecheck_jsr_t *, state.basicblockcount);
+
+ /* initialize stack of first block */
+
+ state.indepth[0] = 0;
+
+ /* initialize locals of first block */
+
+ /* if this is an instance method initialize the "this" ref type */
+
+ if (!(state.m->flags & ACC_STATIC)) {
+ if (state.validlocals < 1)
+ VERIFY_ERROR("Not enough local variables for method arguments");
+ dst = state.startlocals;
+ dst->type = TYPE_ADR;
+ if (state.initmethod)
+ TYPEINFO_INIT_NEWOBJECT(dst->typeinfo, NULL);
+ else
+ typeinfo_init_classinfo(&(dst->typeinfo), state.m->clazz);
+
+ skip = 1;
+ }
+
+ LOG("'this' argument set.\n");
+
+ len = typedescriptors_init_from_methoddesc(state.startlocals + skip,
+ state.m->parseddesc,
+ state.validlocals, true, skip, &state.returntype);
+ if (len < 0)
+ return false;
+
+ /* set remaining locals to void */
+
+ for (i = skip + len; i<state.numlocals; ++i)
+ state.startlocals[i].type = TYPE_VOID;
+
+ /* initialize block flags */
+
+ typecheck_init_flags(&state, BBUNDEF);
+
+ /* iterate until fixpoint reached */
+
+ do {
+
+ state.repeat = false;
+
+ /* iterate over the basic blocks */
+
+ for (state.bptr = state.basicblocks; state.bptr != NULL; state.bptr = state.bptr->next) {
+
+ if (state.bptr->flags != BBTYPECHECK_REACHED)
+ continue;
+
+ DOLOG( show_basicblock(jd, state.bptr, SHOW_PARSE); );
+
+ /* mark this block as analysed */
+
+ state.bptr->flags = BBFINISHED;
+
+ /* determine the active exception handlers for this block */
+ /* XXX could use a faster algorithm with sorted lists or */
+ /* something? */
+ /* XXX reuse code from variables based verifer? */
+ len = 0;
+ for (ex = STATE->jd->exceptiontable; ex ; ex = ex->down) {
+ if ((ex->start->nr <= STATE->bptr->nr) && (ex->end->nr > STATE->bptr->nr)) {
+ LOG1("\tactive handler L%03d", ex->handler->nr);
+ STATE->handlers[len++] = ex;
+ }
+ }
+ STATE->handlers[len] = NULL;
+
+ /* initialize the locals */
+
+ MCOPY(state.locals,
+ state.startlocals + (state.bptr->nr * state.numlocals),
+ verifier_slot_t, state.numlocals);
+
+ /* initialize the stack */
+
+ len = state.indepth[state.bptr->nr];
+
+ MCOPY(stackfloor,
+ state.startstack + (state.bptr->nr * state.m->maxstack),
+ verifier_slot_t, len);
+
+ stack = stackfloor + (len - 1);
+
+ /* iterate over the instructions in this block */
+
+ state.iptr = state.bptr->iinstr;
+ len = state.bptr->icount;
+
+ superblockend = false;
+
+ for (; len--; state.iptr++) {
+
+ maythrow = false;
+
+ LOGNL;
+ DOLOG( typecheck_stackbased_show_state(&state, stack, stackfloor, true); );
+
+ switch (state.iptr->opc) {
+#define TYPECHECK_STACKBASED 1
+#define STATE (&state)
+#define IPTR state.iptr
+#define BPTR state.bptr
+#define METHOD state.m
+#define LOCAL_SLOT(index) (state.locals + (index))
+#define EXCEPTION \
+ do { \
+ LOG("EXCEPTION THROWN!\n"); \
+ return false; \
+ } while (0)
+
+#include <typecheck-stackbased-gen.inc>
+#undef TYPECHECK_STACKBASED
+ }
+
+ /* reach exception handlers for this instruction */
+
+ if (maythrow) {
+ TYPECHECK_COUNT(stat_ins_maythrow);
+ TYPECHECK_MARK(STATE->stat_maythrow);
+ LOG("\treaching exception handlers");
+ i = 0;
+ while (STATE->handlers[i]) {
+ TYPECHECK_COUNT(stat_handlers_reached);
+ if (STATE->handlers[i]->catchtype.any)
+ exstack.typeinfo.typeclass = STATE->handlers[i]->catchtype;
+ else
+ exstack.typeinfo.typeclass.cls = class_java_lang_Throwable;
+ if (!typecheck_stackbased_reach(
+ STATE,
+ STATE->handlers[i]->handler,
+ &exstack, 1))
+ EXCEPTION;
+ i++;
+ }
+ }
+ }
+
+ /* propagate types to the following block */
+
+ if (!superblockend) {
+ if (!typecheck_stackbased_reach(&state, state.bptr->next,
+ stack, stack - stackfloor + 1))
+ EXCEPTION;
+ }
+ } /* end loop over blocks */
+
+ while (!state.repeat && state.topjsr) {
+ LOG1("done analysing subroutine L%03d", state.topjsr->start->nr);
+
+ /* propagate down used locals */
+
+ if (state.topjsr->next) {
+ for (i=0; i<state.numlocals; ++i)
+ state.topjsr->next->usedlocals[i] |= state.topjsr->usedlocals[i];
+ }
+
+ /* restore REACHED flags */
+
+ for (tbptr = state.basicblocks; tbptr != NULL; tbptr = tbptr->next) {
+ assert(tbptr->flags != BBTYPECHECK_REACHED);
+ if (state.topjsr->blockflags[tbptr->nr] == BBTYPECHECK_REACHED) {
+ tbptr->flags = BBTYPECHECK_REACHED;
+ state.repeat = true;
+ }
+ }
+
+ /* dactivate the subroutine */
+
+ state.topjsr->active = false;
+ state.topjsr = state.topjsr->next;
+ }
+ } while (state.repeat);
+
+ /* reset block flags */
+
+ typecheck_reset_flags(&state);
+
+ LOG("typecheck_stackbased successful");
+
+ return true;
+
+throw_stack_underflow:
+ LOG("STACK UNDERFLOW!");
+ exceptions_throw_verifyerror(state.m, "Unable to pop operand off an empty stack");
+ return false;
+
+throw_stack_overflow:
+ LOG("STACK OVERFLOW!");
+ exceptions_throw_verifyerror(state.m, "Stack size too large");
+ return false;
+
+throw_stack_type_error:
+ LOG("STACK TYPE ERROR!");
+ exceptions_throw_verifyerror(state.m, "Mismatched stack types");
+ return false;
+
+throw_local_type_error:
+ LOG("LOCAL TYPE ERROR!");
+ exceptions_throw_verifyerror(state.m, "Local variable type mismatch");
+ return false;
+
+throw_stack_category_error:
+ LOG("STACK CATEGORY ERROR!");
+ exceptions_throw_verifyerror(state.m, "Attempt to split long or double on the stack");
+ return false;
+}
+
+
+#if defined(TYPECHECK_VERBOSE)
+static void typecheck_stackbased_show_state(verifier_state *state,
+ typedescriptor_t *stack,
+ typedescriptor_t *stackfloor,
+ bool showins)
+{
+ typedescriptor_t *sp;
+ s4 i;
+
+ LOGSTR1("stackdepth %d stack [", (stack - stackfloor) + 1);
+ for (sp=stackfloor; sp <= stack; sp++) {
+ LOGSTR(" ");
+ DOLOG( typedescriptor_print(stdout, sp); );
+ }
+ LOGSTR(" ] locals [");
+ for (i=0; i<state->numlocals; ++i) {
+ LOGSTR(" ");
+ DOLOG( typedescriptor_print(stdout, state->locals + i); );
+ }
+ LOGSTR(" ]");
+ LOGNL;
+ if (showins && state->iptr < (state->bptr->iinstr + state->bptr->icount)) {
+ LOGSTR("\t");
+ DOLOG( show_icmd(state->jd, state->iptr, false, SHOW_PARSE); );
+ LOGNL;
+ }
+}
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* defined(ENABLE_VERIFIER) */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+
+++ /dev/null
-/* src/vm/jit/verify/typecheck-typeinferer.c - type inference pass
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-#include "config.h"
-#include "vm/types.h"
-#include "vm/global.h"
-
-#include <assert.h>
-#include <string.h>
-
-#include "mm/memory.h"
-
-#include "native/native.hpp"
-
-#include "toolbox/logging.h"
-
-#include "vm/access.h"
-#include "vm/array.hpp"
-#include "vm/jit/builtin.hpp"
-#include "vm/exceptions.hpp"
-#include "vm/globals.hpp"
-#include "vm/loader.hpp"
-#include "vm/options.h"
-#include "vm/primitive.hpp"
-#include "vm/resolve.hpp"
-#include "vm/vm.hpp"
-
-#include "vm/jit/jit.hpp"
-#include "vm/jit/show.hpp"
-#include "vm/jit/parse.h"
-
-#include "vm/jit/verify/typecheck-typeinferer.h"
-
-#define TYPECHECK_NO_STATISTICS
-#include <typecheck-common.h>
-
-
-/* macros used by the generated code ******************************************/
-
-#define EXCEPTION do { return false; } while (0)
-#define VERIFY_ERROR(msg) assert(false)
-
-#define CHECK_LOCAL_TYPE(index, t) \
- assert(jd->var[(index)].type == (t));
-
-#define STORE_LOCAL(t, index) \
- do { \
- typevector_store(jd->var, (index), (t), NULL); \
- } while (0)
-
-#define STORE_LOCAL_2_WORD(t, index) \
- do { \
- typevector_store(jd->var, (index), (t), NULL); \
- } while (0)
-
-#define REACH_BLOCK(target) \
- do { \
- if (!typestate_reach(state, (target), \
- state->bptr->outvars, jd->var, \
- state->bptr->outdepth)) \
- return false; \
- } while (0)
-
-#define REACH(target) REACH_BLOCK((target).block)
-
-#define TYPECHECK_INT(v) assert(jd->var[(v)].type == TYPE_INT)
-#define TYPECHECK_ADR(v) assert(jd->var[(v)].type == TYPE_ADR)
-
-
-/* handle_fieldaccess **********************************************************
-
- Verify an ICMD_{GET,PUT}{STATIC,FIELD}(CONST)?
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_fieldaccess(verifier_state *state,
- varinfo *instance,
- varinfo *value)
-{
- jitdata *jd;
-
- jd = state->jd;
-
-#define TYPECHECK_TYPEINFERER
-#include <typecheck-fields.inc>
-#undef TYPECHECK_TYPEINFERER
-
- return true;
-}
-
-
-/* handle_invocation ***********************************************************
-
- Verify an ICMD_INVOKE* instruction.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_invocation(verifier_state *state)
-{
- jitdata *jd;
- varinfo *dv; /* output variable of current instruction */
-
- jd = state->jd;
- dv = VAROP(state->iptr->dst);
-
-#define TYPECHECK_TYPEINFERER
-#define OP1 VAR(state->iptr->sx.s23.s2.args[0])
-#include <typecheck-invoke.inc>
-#undef OP1
-#undef TYPECHECK_TYPEINFERER
-
- return true;
-}
-
-
-/* handle_builtin **************************************************************
-
- Verify the call of a builtin method.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_builtin(verifier_state *state)
-{
- jitdata *jd;
- varinfo *dv; /* output variable of current instruction */
-
- jd = state->jd;
- dv = VAROP(state->iptr->dst);
-
-#define TYPECHECK_TYPEINFERER
-#define OP1 state->iptr->sx.s23.s2.args[0]
-#include <typecheck-builtins.inc>
-#undef OP1
-#undef TYPECHECK_TYPEINFERER
-
- return true;
-}
-
-/* handle_multianewarray *******************************************************
-
- Verify a MULTIANEWARRAY instruction.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_multianewarray(verifier_state *state)
-{
- jitdata *jd;
- varinfo *dv; /* output variable of current instruction */
-
- jd = state->jd;
- dv = VAROP(state->iptr->dst);
-
-#define TYPECHECK_TYPEINFERER
-#include <typecheck-multianewarray.inc>
-#undef TYPECHECK_TYPEINFERER
-
- return true;
-}
-
-
-/* handle_basic_block **********************************************************
-
- Perform bytecode verification of a basic block.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_basic_block(verifier_state *state)
-{
- int opcode; /* current opcode */
- int len; /* for counting instructions, etc. */
- bool superblockend; /* true if no fallthrough to next block */
- instruction *iptr; /* the current instruction */
- basicblock *tbptr; /* temporary for target block */
- bool maythrow; /* true if this instruction may throw */
- s4 i;
- branch_target_t *table;
- lookup_target_t *lookup;
- jitdata *jd = state->jd;
- exception_entry *ex;
-
- LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",state->bptr->nr);
- LOGFLUSH;
- DOLOG(show_basicblock(jd, state->bptr, SHOW_STACK));
-
- superblockend = false;
- state->bptr->flags = BBFINISHED;
-
- /* prevent compiler warnings */
-
-
- /* determine the active exception handlers for this block */
- /* XXX could use a faster algorithm with sorted lists or */
- /* something? */
- len = 0;
- for (ex = state->jd->exceptiontable; ex ; ex = ex->down) {
- if ((ex->start->nr <= state->bptr->nr) && (ex->end->nr > state->bptr->nr)) {
- LOG1("active handler L%03d", ex->handler->nr);
- state->handlers[len++] = ex;
- }
- }
- state->handlers[len] = NULL;
-
- /* init variable types at the start of this block */
- typevector_copy_inplace(state->bptr->inlocals, jd->var, state->numlocals);
-
- DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->invars,
- state->bptr->indepth));
- DOLOG(typevector_print(stdout, jd->var, state->numlocals));
- LOGNL; LOGFLUSH;
-
- /* loop over the instructions */
- len = state->bptr->icount;
- state->iptr = state->bptr->iinstr;
- while (--len >= 0) {
- TYPECHECK_COUNT(stat_ins);
-
- iptr = state->iptr;
-
- DOLOG(typevector_print(stdout, jd->var, state->numlocals));
- LOGNL; LOGFLUSH;
- DOLOG(show_icmd(jd, state->iptr, false, SHOW_STACK)); LOGNL; LOGFLUSH;
-
- opcode = iptr->opc;
- maythrow = false;
-
- switch (opcode) {
-
- /* include generated code for ICMDs verification */
-
-#define TYPECHECK_TYPEINFERER
-#define STATE state
-#define METHOD (state->m)
-#define IPTR iptr
-#define BPTR (state->bptr)
-#include <typecheck-typeinferer-gen.inc>
-#undef STATE
-#undef METHOD
-#undef IPTR
-#undef BPTR
-#undef TYPECHECK_TYPEINFERER
-
- default:
- vm_abort("missing ICMD in type inferer: %d\n", opcode);
- }
-
- /* reach exception handlers for this instruction */
-
- if (maythrow) {
- TYPECHECK_COUNT(stat_ins_maythrow);
- TYPECHECK_MARK(state->stat_maythrow);
- LOG("reaching exception handlers");
- i = 0;
- while (state->handlers[i]) {
- TYPECHECK_COUNT(stat_handlers_reached);
- if (state->handlers[i]->catchtype.any)
- VAR(state->exinvars)->typeinfo.typeclass = state->handlers[i]->catchtype;
- else
- VAR(state->exinvars)->typeinfo.typeclass.cls = class_java_lang_Throwable;
- if (!typestate_reach(state,
- state->handlers[i]->handler,
- &(state->exinvars), jd->var, 1))
- return false;
- i++;
- }
- }
-
- LOG("\t\tnext instruction");
- state->iptr++;
- } /* while instructions */
-
- LOG("instructions done");
- LOGSTR("RESULT=> ");
- DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->outvars,
- state->bptr->outdepth));
- DOLOG(typevector_print(stdout, jd->var, state->numlocals));
- LOGNL; LOGFLUSH;
-
- /* propagate stack and variables to the following block */
- if (!superblockend) {
- LOG("reaching following block");
- tbptr = state->bptr->next;
- while (tbptr->flags == BBDELETED) {
- tbptr = tbptr->next;
- }
- if (!typestate_reach(state,tbptr,state->bptr->outvars, jd->var,
- state->bptr->outdepth))
- return false;
- }
-
- return true;
-}
-
-
-bool typecheck_infer_types(jitdata *jd)
-{
- methodinfo *meth;
- codegendata *cd;
- varinfo *savedlocals;
- verifier_state state; /* current state of the verifier */
-
- /* get required compiler data */
-
- meth = jd->m;
- cd = jd->cd;
-
- /* some logging on entry */
-
-
- LOGSTR("\n==============================================================================\n");
- DOLOG( show_method(jd, SHOW_STACK) );
- LOGSTR("\n==============================================================================\n");
- LOGMETHOD("Entering type inference: ",cd->method);
-
- /* initialize the verifier state */
-
- state.m = meth;
- state.jd = jd;
- state.cd = cd;
- state.basicblockcount = jd->basicblockcount;
- state.basicblocks = jd->basicblocks;
- state.savedindices = NULL;
- state.savedinvars = NULL;
-
- /* check that the basicblock numbers are valid */
-
-#if !defined(NDEBUG)
- jit_check_basicblock_numbers(jd);
-#endif
-
- /* check if this method is an instance initializer method */
-
- state.initmethod = (state.m->name == utf_init);
-
- /* initialize the basic block flags for the following CFG traversal */
-
- typecheck_init_flags(&state, BBFINISHED);
-
- /* number of local variables */
-
- /* In <init> methods we use an extra local variable to indicate whether */
- /* the 'this' reference has been initialized. */
- /* TYPE_VOID...means 'this' has not been initialized, */
- /* TYPE_INT....means 'this' has been initialized. */
-
- state.numlocals = state.jd->localcount;
- state.validlocals = state.numlocals;
- if (state.initmethod)
- state.numlocals++; /* VERIFIER_EXTRA_LOCALS */
-
- /* allocate the buffer of active exception handlers */
-
- state.handlers = DMNEW(exception_entry*, state.jd->exceptiontablelength + 1);
-
- /* save local variables */
-
- savedlocals = DMNEW(varinfo, state.numlocals);
- MCOPY(savedlocals, jd->var, varinfo, state.numlocals);
-
- /* initialized local variables of first block */
-
- if (!typecheck_init_locals(&state, false))
- return false;
-
- /* initialize invars of exception handlers */
-
- state.exinvars = state.numlocals;
- VAR(state.exinvars)->type = TYPE_ADR;
- typeinfo_init_classinfo(&(VAR(state.exinvars)->typeinfo),
- class_java_lang_Throwable); /* changed later */
-
- LOG("Exception handler stacks set.\n");
-
- /* loop while there are still blocks to be checked */
- do {
- TYPECHECK_COUNT(count_iterations);
-
- state.repeat = false;
-
- state.bptr = state.basicblocks;
-
- for (; state.bptr; state.bptr = state.bptr->next) {
- LOGSTR1("---- BLOCK %04d, ",state.bptr->nr);
- LOGSTR1("blockflags: %d\n",state.bptr->flags);
- LOGFLUSH;
-
- /* verify reached block */
- if (state.bptr->flags == BBTYPECHECK_REACHED) {
- if (!handle_basic_block(&state))
- return false;
- }
- } /* for blocks */
-
- LOGIF(state.repeat,"state.repeat == true");
- } while (state.repeat);
-
- /* statistics */
-
- /* reset the flags of blocks we haven't reached */
-
- typecheck_reset_flags(&state);
-
- /* restore locals */
-
- MCOPY(jd->var, savedlocals, varinfo, state.numlocals);
-
- /* everything's ok */
-
- LOGimp("exiting type inference");
- return true;
-}
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/verify/typecheck-typeinferer.c - type inference pass
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+#include "config.h"
+#include "vm/types.h"
+#include "vm/global.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "mm/memory.h"
+
+#include "native/native.hpp"
+
+#include "toolbox/logging.h"
+
+#include "vm/access.hpp"
+#include "vm/array.hpp"
+#include "vm/jit/builtin.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/globals.hpp"
+#include "vm/loader.hpp"
+#include "vm/options.h"
+#include "vm/primitive.hpp"
+#include "vm/resolve.hpp"
+#include "vm/vm.hpp"
+
+#include "vm/jit/jit.hpp"
+#include "vm/jit/show.hpp"
+#include "vm/jit/parse.hpp"
+
+#include "vm/jit/verify/typecheck-typeinferer.hpp"
+
+#define TYPECHECK_NO_STATISTICS
+#include "vm/jit/verify/typecheck-common.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* macros used by the generated code ******************************************/
+
+#define EXCEPTION do { return false; } while (0)
+#define VERIFY_ERROR(msg) assert(false)
+
+#define CHECK_LOCAL_TYPE(index, t) \
+ assert(jd->var[(index)].type == (t));
+
+#define STORE_LOCAL(t, index) \
+ do { \
+ typevector_store(jd->var, (index), (t), NULL); \
+ } while (0)
+
+#define STORE_LOCAL_2_WORD(t, index) \
+ do { \
+ typevector_store(jd->var, (index), (t), NULL); \
+ } while (0)
+
+#define REACH_BLOCK(target) \
+ do { \
+ if (!typestate_reach(state, (target), \
+ state->bptr->outvars, jd->var, \
+ state->bptr->outdepth)) \
+ return false; \
+ } while (0)
+
+#define REACH(target) REACH_BLOCK((target).block)
+
+#define TYPECHECK_INT(v) assert(jd->var[(v)].type == TYPE_INT)
+#define TYPECHECK_ADR(v) assert(jd->var[(v)].type == TYPE_ADR)
+
+
+/* handle_fieldaccess **********************************************************
+
+ Verify an ICMD_{GET,PUT}{STATIC,FIELD}(CONST)?
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_fieldaccess(verifier_state *state,
+ varinfo *instance,
+ varinfo *value)
+{
+ jitdata *jd;
+
+ jd = state->jd;
+
+#define TYPECHECK_TYPEINFERER
+#include <typecheck-fields.inc>
+#undef TYPECHECK_TYPEINFERER
+
+ return true;
+}
+
+
+/* handle_invocation ***********************************************************
+
+ Verify an ICMD_INVOKE* instruction.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_invocation(verifier_state *state)
+{
+ jitdata *jd;
+ varinfo *dv; /* output variable of current instruction */
+
+ jd = state->jd;
+ dv = VAROP(state->iptr->dst);
+
+#define TYPECHECK_TYPEINFERER
+#define OP1 VAR(state->iptr->sx.s23.s2.args[0])
+#include <typecheck-invoke.inc>
+#undef OP1
+#undef TYPECHECK_TYPEINFERER
+
+ return true;
+}
+
+
+/* handle_builtin **************************************************************
+
+ Verify the call of a builtin method.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_builtin(verifier_state *state)
+{
+ jitdata *jd;
+ varinfo *dv; /* output variable of current instruction */
+
+ jd = state->jd;
+ dv = VAROP(state->iptr->dst);
+
+#define TYPECHECK_TYPEINFERER
+#define OP1 state->iptr->sx.s23.s2.args[0]
+#include <typecheck-builtins.inc>
+#undef OP1
+#undef TYPECHECK_TYPEINFERER
+
+ return true;
+}
+
+/* handle_multianewarray *******************************************************
+
+ Verify a MULTIANEWARRAY instruction.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_multianewarray(verifier_state *state)
+{
+ jitdata *jd;
+ varinfo *dv; /* output variable of current instruction */
+
+ jd = state->jd;
+ dv = VAROP(state->iptr->dst);
+
+#define TYPECHECK_TYPEINFERER
+#include <typecheck-multianewarray.inc>
+#undef TYPECHECK_TYPEINFERER
+
+ return true;
+}
+
+
+/* handle_basic_block **********************************************************
+
+ Perform bytecode verification of a basic block.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_basic_block(verifier_state *state)
+{
+ int opcode; /* current opcode */
+ int len; /* for counting instructions, etc. */
+ bool superblockend; /* true if no fallthrough to next block */
+ instruction *iptr; /* the current instruction */
+ basicblock *tbptr; /* temporary for target block */
+ bool maythrow; /* true if this instruction may throw */
+ s4 i;
+ branch_target_t *table;
+ lookup_target_t *lookup;
+ jitdata *jd = state->jd;
+ exception_entry *ex;
+
+ LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",state->bptr->nr);
+ LOGFLUSH;
+ DOLOG(show_basicblock(jd, state->bptr, SHOW_STACK));
+
+ superblockend = false;
+ state->bptr->flags = BBFINISHED;
+
+ /* prevent compiler warnings */
+
+
+ /* determine the active exception handlers for this block */
+ /* XXX could use a faster algorithm with sorted lists or */
+ /* something? */
+ len = 0;
+ for (ex = state->jd->exceptiontable; ex ; ex = ex->down) {
+ if ((ex->start->nr <= state->bptr->nr) && (ex->end->nr > state->bptr->nr)) {
+ LOG1("active handler L%03d", ex->handler->nr);
+ state->handlers[len++] = ex;
+ }
+ }
+ state->handlers[len] = NULL;
+
+ /* init variable types at the start of this block */
+ typevector_copy_inplace(state->bptr->inlocals, jd->var, state->numlocals);
+
+ DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->invars,
+ state->bptr->indepth));
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
+ LOGNL; LOGFLUSH;
+
+ /* loop over the instructions */
+ len = state->bptr->icount;
+ state->iptr = state->bptr->iinstr;
+ while (--len >= 0) {
+ TYPECHECK_COUNT(stat_ins);
+
+ iptr = state->iptr;
+
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
+ LOGNL; LOGFLUSH;
+ DOLOG(show_icmd(jd, state->iptr, false, SHOW_STACK)); LOGNL; LOGFLUSH;
+
+ opcode = iptr->opc;
+ maythrow = false;
+
+ switch (opcode) {
+
+ /* include generated code for ICMDs verification */
+
+#define TYPECHECK_TYPEINFERER
+#define STATE state
+#define METHOD (state->m)
+#define IPTR iptr
+#define BPTR (state->bptr)
+#include <typecheck-typeinferer-gen.inc>
+#undef STATE
+#undef METHOD
+#undef IPTR
+#undef BPTR
+#undef TYPECHECK_TYPEINFERER
+
+ default:
+ vm_abort("missing ICMD in type inferer: %d\n", opcode);
+ }
+
+ /* reach exception handlers for this instruction */
+
+ if (maythrow) {
+ TYPECHECK_COUNT(stat_ins_maythrow);
+ TYPECHECK_MARK(state->stat_maythrow);
+ LOG("reaching exception handlers");
+ i = 0;
+ while (state->handlers[i]) {
+ TYPECHECK_COUNT(stat_handlers_reached);
+ if (state->handlers[i]->catchtype.any)
+ VAR(state->exinvars)->typeinfo.typeclass = state->handlers[i]->catchtype;
+ else
+ VAR(state->exinvars)->typeinfo.typeclass.cls = class_java_lang_Throwable;
+ if (!typestate_reach(state,
+ state->handlers[i]->handler,
+ &(state->exinvars), jd->var, 1))
+ return false;
+ i++;
+ }
+ }
+
+ LOG("\t\tnext instruction");
+ state->iptr++;
+ } /* while instructions */
+
+ LOG("instructions done");
+ LOGSTR("RESULT=> ");
+ DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->outvars,
+ state->bptr->outdepth));
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
+ LOGNL; LOGFLUSH;
+
+ /* propagate stack and variables to the following block */
+ if (!superblockend) {
+ LOG("reaching following block");
+ tbptr = state->bptr->next;
+ while (tbptr->flags == BBDELETED) {
+ tbptr = tbptr->next;
+ }
+ if (!typestate_reach(state,tbptr,state->bptr->outvars, jd->var,
+ state->bptr->outdepth))
+ return false;
+ }
+
+ return true;
+}
+
+
+bool typecheck_infer_types(jitdata *jd)
+{
+ methodinfo *meth;
+ codegendata *cd;
+ varinfo *savedlocals;
+ verifier_state state; /* current state of the verifier */
+
+ /* get required compiler data */
+
+ meth = jd->m;
+ cd = jd->cd;
+
+ /* some logging on entry */
+
+
+ LOGSTR("\n==============================================================================\n");
+ DOLOG( show_method(jd, SHOW_STACK) );
+ LOGSTR("\n==============================================================================\n");
+ LOGMETHOD("Entering type inference: ",cd->method);
+
+ /* initialize the verifier state */
+
+ state.m = meth;
+ state.jd = jd;
+ state.cd = cd;
+ state.basicblockcount = jd->basicblockcount;
+ state.basicblocks = jd->basicblocks;
+ state.savedindices = NULL;
+ state.savedinvars = NULL;
+
+ /* check that the basicblock numbers are valid */
+
+#if !defined(NDEBUG)
+ jit_check_basicblock_numbers(jd);
+#endif
+
+ /* check if this method is an instance initializer method */
+
+ state.initmethod = (state.m->name == utf_init);
+
+ /* initialize the basic block flags for the following CFG traversal */
+
+ typecheck_init_flags(&state, BBFINISHED);
+
+ /* number of local variables */
+
+ /* In <init> methods we use an extra local variable to indicate whether */
+ /* the 'this' reference has been initialized. */
+ /* TYPE_VOID...means 'this' has not been initialized, */
+ /* TYPE_INT....means 'this' has been initialized. */
+
+ state.numlocals = state.jd->localcount;
+ state.validlocals = state.numlocals;
+ if (state.initmethod)
+ state.numlocals++; /* VERIFIER_EXTRA_LOCALS */
+
+ /* allocate the buffer of active exception handlers */
+
+ state.handlers = (exception_entry**) DumpMemory::allocate(sizeof(exception_entry*) * (state.jd->exceptiontablelength + 1));
+
+ /* save local variables */
+
+ savedlocals = (varinfo*) DumpMemory::allocate(sizeof(varinfo) * state.numlocals);
+ MCOPY(savedlocals, jd->var, varinfo, state.numlocals);
+
+ /* initialized local variables of first block */
+
+ if (!typecheck_init_locals(&state, false))
+ return false;
+
+ /* initialize invars of exception handlers */
+
+ state.exinvars = state.numlocals;
+ VAR(state.exinvars)->type = TYPE_ADR;
+ typeinfo_init_classinfo(&(VAR(state.exinvars)->typeinfo),
+ class_java_lang_Throwable); /* changed later */
+
+ LOG("Exception handler stacks set.\n");
+
+ /* loop while there are still blocks to be checked */
+ do {
+ TYPECHECK_COUNT(count_iterations);
+
+ state.repeat = false;
+
+ state.bptr = state.basicblocks;
+
+ for (; state.bptr; state.bptr = state.bptr->next) {
+ LOGSTR1("---- BLOCK %04d, ",state.bptr->nr);
+ LOGSTR1("blockflags: %d\n",state.bptr->flags);
+ LOGFLUSH;
+
+ /* verify reached block */
+ if (state.bptr->flags == BBTYPECHECK_REACHED) {
+ if (!handle_basic_block(&state))
+ return false;
+ }
+ } /* for blocks */
+
+ LOGIF(state.repeat,"state.repeat == true");
+ } while (state.repeat);
+
+ /* statistics */
+
+ /* reset the flags of blocks we haven't reached */
+
+ typecheck_reset_flags(&state);
+
+ /* restore locals */
+
+ MCOPY(jd->var, savedlocals, varinfo, state.numlocals);
+
+ /* everything's ok */
+
+ LOGimp("exiting type inference");
+ return true;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typecheck-typeinferer.h - type inference header
-
- Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
- C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
- E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
- J. Wenninger, Institut f. Computersprachen - TU Wien
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
- Contact: cacao@cacaojvm.org
-
- Authors: Edwin Steiner
-
-
-*/
-
-
-#ifndef _TYPECHECK_TYPEINFERER_H
-#define _TYPECHECK_TYPEINFERER_H
-
-#include "config.h"
-
-#include "vm/global.h"
-#include "vm/jit/jit.hpp"
-
-
-/* function prototypes ********************************************************/
-
-#if defined(ENABLE_VERIFIER)
-bool typecheck_infer_types(jitdata *jd);
-#endif
-
-#endif /* _TYPECHECK_TYPEINFERER_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
--- /dev/null
+/* src/vm/jit/verify/typecheck-typeinferer.h - type inference header
+
+ Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
+ C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
+ E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
+ J. Wenninger, Institut f. Computersprachen - TU Wien
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Contact: cacao@cacaojvm.org
+
+ Authors: Edwin Steiner
+
+
+*/
+
+
+#ifndef _TYPECHECK_TYPEINFERER_H
+#define _TYPECHECK_TYPEINFERER_H
+
+#include "config.h"
+
+#include "vm/global.h"
+#include "vm/jit/jit.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* function prototypes ********************************************************/
+
+#if defined(ENABLE_VERIFIER)
+bool typecheck_infer_types(jitdata *jd);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* _TYPECHECK_TYPEINFERER_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typecheck.c - typechecking (part of bytecode verification)
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-/*
-
-What's the purpose of the `typechecker`?
-----------------------------------------
-
-The typechecker analyses (the intermediate repr. of) the bytecode of
-each method and ensures that for each instruction the values on the
-stack and in local variables are of the correct type whenever the
-instruction is executed.
-
-type checking is a mandatory part of bytecode verification.
-
-
-How does the typechecker work?
-------------------------------
-
-The JVM stack and the local variables are not statically typed, so the
-typechecker has to *infer* the static types of stack slots and local
-variables at each point of the method. The JVM spec imposes a lot of
-restrictions on the bytecode in order to guarantee that this is always
-possible.
-
-Basically the typechecker solves the data flow equations of the method.
-This is done in the usual way for a forward data flow analysis: Starting
-from the entry point of the method the typechecker follows the CFG and
-records the type of each stack slot and local variable at each point[1].
-When two or more control flow paths merge at a point, the union of the
-types for each slot/variable is taken. The algorithm continues to follow
-all possible paths[2] until the recorded types do not change anymore (ie.
-the equations have been solved).
-
-If the solution has been reached and the resulting types are valid for
-all instructions, then type checking terminates with success, otherwise
-an exception is thrown.
-
-
-Why is this code so damn complicated?
--------------------------------------
-
-Short answer: The devil's in the details.
-
-While the basic operation of the typechecker is no big deal, there are
-many properties of Java bytecode which make type checking hard. Some of
-them are not even addressed in the JVM spec. Some problems and their
-solutions:
-
-*) Finding a good representation of the union of two reference types is
-difficult because of multiple inheritance of interfaces.
-
- Solution: The typeinfo system can represent such "merged" types by a
- list of proper subclasses of a class. Example:
-
- typeclass=java.lang.Object merged={ InterfaceA, InterfaceB }
-
- represents the result of merging two interface types "InterfaceA"
- and "InterfaceB".
-
-*) When the code of a method is verified, there may still be unresolved
-references to classes/methods/fields in the code, which we may not force
-to be resolved eagerly. (A similar problem arises because of the special
-checks for protected members.)
-
- Solution: The typeinfo system knows how to deal with unresolved
- class references. Whenever a check has to be performed for an
- unresolved type, the type is annotated with constraints representing
- the check. Later, when the type is resolved, the constraints are
- checked. (See the constrain_unresolved_... and the resolve_...
- methods.)[3]
-
-*) Checks for uninitialized object instances are hard because after the
-invocation of <init> on an uninitialized object *all* slots/variables
-referring to this object (and exactly those slots/variables) must be
-marked as initialized.
-
- Solution: The JVM spec describes a solution, which has been
- implemented in this typechecker.
-
-Note that some checks mentioned in the JVM spec are unnecessary[4] and
-not performed by either the reference implementation, or this implementation.
-
-
---- Footnotes
-
-[1] Actually only the types of slots/variables at the start of each
-basic block are remembered. Within a basic block the algorithm only keeps
-the types of the slots/variables for the "current" instruction which is
-being analysed.
-
-[2] Actually the algorithm iterates through the basic block list until
-there are no more changes. Theoretically it would be wise to sort the
-basic blocks topologically beforehand, but the number of average/max
-iterations observed is so low, that this was not deemed necessary.
-
-[3] This is similar to a method proposed by: Alessandro Coglio et al., A
-Formal Specification of Java Class Loading, Technical Report, Kestrel
-Institute April 2000, revised July 2000
-http://www.kestrel.edu/home/people/coglio/loading.pdf
-An important difference is that Coglio's subtype constraints are checked
-after loading, while our constraints are checked when the field/method
-is accessed for the first time, so we can guarantee lexically correct
-error reporting.
-
-[4] Alessandro Coglio
- Improving the official specification of Java bytecode verification
- Proceedings of the 3rd ECOOP Workshop on Formal Techniques for Java Programs
- June 2001
- citeseer.ist.psu.edu/article/coglio03improving.html
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <string.h>
-
-#include "vm/types.h"
-
-#ifdef ENABLE_VERIFIER
-
-#include "mm/memory.h"
-
-#include "native/native.hpp"
-
-#include "toolbox/logging.h"
-
-#include "vm/access.h"
-#include "vm/array.hpp"
-#include "vm/jit/builtin.hpp"
-#include "vm/exceptions.hpp"
-#include "vm/global.h"
-#include "vm/globals.hpp"
-#include "vm/loader.hpp"
-#include "vm/options.h"
-#include "vm/primitive.hpp"
-#include "vm/resolve.hpp"
-
-#include "vm/jit/jit.hpp"
-#include "vm/jit/parse.h"
-#include "vm/jit/show.hpp"
-
-#include <typecheck-common.h>
-
-
-/****************************************************************************/
-/* MACROS FOR VARIABLE TYPE CHECKING */
-/****************************************************************************/
-
-#define TYPECHECK_CHECK_TYPE(i,tp,msg) \
- do { \
- if (VAR(i)->type != (tp)) { \
- exceptions_throw_verifyerror(state->m, (msg)); \
- return false; \
- } \
- } while (0)
-
-#define TYPECHECK_INT(i) \
- TYPECHECK_CHECK_TYPE(i,TYPE_INT,"Expected to find integer value")
-#define TYPECHECK_LNG(i) \
- TYPECHECK_CHECK_TYPE(i,TYPE_LNG,"Expected to find long value")
-#define TYPECHECK_FLT(i) \
- TYPECHECK_CHECK_TYPE(i,TYPE_FLT,"Expected to find float value")
-#define TYPECHECK_DBL(i) \
- TYPECHECK_CHECK_TYPE(i,TYPE_DBL,"Expected to find double value")
-#define TYPECHECK_ADR(i) \
- TYPECHECK_CHECK_TYPE(i,TYPE_ADR,"Expected to find object value")
-
-#define TYPECHECK_INT_OP(o) TYPECHECK_INT((o).varindex)
-#define TYPECHECK_LNG_OP(o) TYPECHECK_LNG((o).varindex)
-#define TYPECHECK_FLT_OP(o) TYPECHECK_FLT((o).varindex)
-#define TYPECHECK_DBL_OP(o) TYPECHECK_DBL((o).varindex)
-#define TYPECHECK_ADR_OP(o) TYPECHECK_ADR((o).varindex)
-
-
-/* typestate_save_invars *******************************************************
-
- Save the invars of the current basic block in the space reserved by
- parse.
-
- This function must be called before an instruction modifies a variable
- that is an invar of the current block. In such cases the invars of the
- block must be saved, and restored at the end of the analysis of this
- basic block, so that the invars again reflect the *input* to this basic
- block (and do not randomly contain types that appear within the block).
-
- IN:
- state............current state of the verifier
-
-*******************************************************************************/
-
-static void
-typestate_save_invars(verifier_state *state)
-{
- s4 i, index;
- s4 *pindex;
-
- LOG("saving invars");
-
- if (!state->savedindices) {
- LOG("allocating savedindices buffer");
- pindex = DMNEW(s4, state->m->maxstack);
- state->savedindices = pindex;
- index = state->numlocals + VERIFIER_EXTRA_VARS;
- for (i=0; i<state->m->maxstack; ++i)
- *pindex++ = index++;
- }
-
- /* save types */
-
- typecheck_copy_types(state, state->bptr->invars, state->savedindices,
- state->bptr->indepth);
-
- /* set the invars of the block to the saved variables */
- /* and remember the original invars */
-
- state->savedinvars = state->bptr->invars;
- state->bptr->invars = state->savedindices;
-}
-
-
-/* typestate_restore_invars ***************************************************
-
- Restore the invars of the current basic block that have been previously
- saved by `typestate_save_invars`.
-
- IN:
- state............current state of the verifier
-
-*******************************************************************************/
-
-static void
-typestate_restore_invars(verifier_state *state)
-{
- TYPECHECK_COUNT(stat_savedstack);
- LOG("restoring saved invars");
-
- /* restore the invars pointer */
-
- state->bptr->invars = state->savedinvars;
-
- /* copy the types back */
-
- typecheck_copy_types(state, state->savedindices, state->bptr->invars,
- state->bptr->indepth);
-
- /* mark that there are no saved invars currently */
-
- state->savedinvars = NULL;
-}
-
-
-/* handle_fieldaccess **********************************************************
-
- Verify an ICMD_{GET,PUT}{STATIC,FIELD}(CONST)?
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_fieldaccess(verifier_state *state,
- varinfo *instance,
- varinfo *value)
-{
- jitdata *jd;
-
- jd = state->jd;
-
-#define TYPECHECK_VARIABLESBASED
-#define EXCEPTION do { return false; } while (0)
-#define VERIFY_ERROR(msg) TYPECHECK_VERIFYERROR_bool(msg)
-#include <typecheck-fields.inc>
-#undef EXCEPTION
-#undef VERIFY_ERROR
-#undef TYPECHECK_VARIABLESBASED
-
- return true;
-}
-
-
-/* handle_invocation ***********************************************************
-
- Verify an ICMD_INVOKE* instruction.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_invocation(verifier_state *state)
-{
- jitdata *jd;
- varinfo *dv; /* output variable of current instruction */
-
- jd = state->jd;
- dv = VAROP(state->iptr->dst);
-
-#define TYPECHECK_VARIABLESBASED
-#define OP1 VAR(state->iptr->sx.s23.s2.args[0])
-#include <typecheck-invoke.inc>
-#undef OP1
-#undef TYPECHECK_VARIABLESBASED
-
- return true;
-}
-
-
-/* handle_builtin **************************************************************
-
- Verify the call of a builtin method.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_builtin(verifier_state *state)
-{
- jitdata *jd;
- varinfo *dv; /* output variable of current instruction */
-
- jd = state->jd;
- dv = VAROP(state->iptr->dst);
-
-#define TYPECHECK_VARIABLESBASED
-#define OP1 state->iptr->sx.s23.s2.args[0]
-#include <typecheck-builtins.inc>
-#undef OP1
-#undef TYPECHECK_VARIABLESBASED
-
- return true;
-}
-
-/* handle_multianewarray *******************************************************
-
- Verify a MULTIANEWARRAY instruction.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_multianewarray(verifier_state *state)
-{
- jitdata *jd;
- varinfo *dv; /* output variable of current instruction */
-
- jd = state->jd;
- dv = VAROP(state->iptr->dst);
-
-#define TYPECHECK_VARIABLESBASED
-#define VERIFY_ERROR(msg) TYPECHECK_VERIFYERROR_bool(msg)
-#include <typecheck-multianewarray.inc>
-#undef VERIFY_ERROR
-#undef TYPECHECK_VARIABLESBASED
-
- return true;
-}
-
-/* typecheck_invalidate_locals *************************************************
-
- Invalidate locals that are overwritten by writing to the given local.
-
- IN:
- state............the current state of the verifier
- index............the index of the local that is written
- twoword..........true, if a two-word type is written
-
-*******************************************************************************/
-
-static void typecheck_invalidate_locals(verifier_state *state, s4 index, bool twoword)
-{
- s4 javaindex;
- s4 t;
- s4 varindex;
- jitdata *jd = state->jd;
- s4 *localmap = jd->local_map;
- varinfo *vars = jd->var;
-
- javaindex = jd->reverselocalmap[index];
-
- /* invalidate locals of two-word type at index javaindex-1 */
-
- if (javaindex > 0) {
- localmap += 5 * (javaindex-1);
- for (t=0; t<5; ++t) {
- varindex = *localmap++;
- if (varindex >= 0 && IS_2_WORD_TYPE(vars[varindex].type)) {
- LOG1("invalidate local %d", varindex);
- vars[varindex].type = TYPE_VOID;
- }
- }
- }
- else {
- localmap += 5 * javaindex;
- }
-
- /* invalidate locals at index javaindex */
-
- for (t=0; t<5; ++t) {
- varindex = *localmap++;
- if (varindex >= 0) {
- LOG1("invalidate local %d", varindex);
- vars[varindex].type = TYPE_VOID;
- }
- }
-
- /* if a two-word type is written, invalidate locals at index javaindex+1 */
-
- if (twoword) {
- for (t=0; t<5; ++t) {
- varindex = *localmap++;
- if (varindex >= 0) {
- LOG1("invalidate local %d", varindex);
- vars[varindex].type = TYPE_VOID;
- }
- }
- }
-}
-
-
-/* macros used by the generated code ******************************************/
-
-#define EXCEPTION do { return false; } while (0)
-#define VERIFY_ERROR(msg) TYPECHECK_VERIFYERROR_bool(msg)
-
-#define CHECK_LOCAL_TYPE(index, t) \
- do { \
- if (!typevector_checktype(jd->var, (index), (t))) \
- VERIFY_ERROR("Local variable type mismatch"); \
- } while (0)
-
-#define STORE_LOCAL(t, index) \
- do { \
- s4 temp_t = (t); \
- typecheck_invalidate_locals(state, (index), false); \
- typevector_store(jd->var, (index), (temp_t), NULL); \
- } while (0)
-
-#define STORE_LOCAL_2_WORD(t, index) \
- do { \
- s4 temp_t = (t); \
- typecheck_invalidate_locals(state, (index), true); \
- typevector_store(jd->var, (index), (temp_t), NULL); \
- } while (0)
-
-#define REACH_BLOCK(target) \
- do { \
- if (!typestate_reach(state, (target), \
- state->bptr->outvars, jd->var, \
- state->bptr->outdepth)) \
- return false; \
- } while (0)
-
-#define REACH(target) REACH_BLOCK((target).block)
-
-
-/* handle_basic_block **********************************************************
-
- Perform bytecode verification of a basic block.
-
- IN:
- state............the current state of the verifier
-
- RETURN VALUE:
- true.............successful verification,
- false............an exception has been thrown.
-
-*******************************************************************************/
-
-static bool
-handle_basic_block(verifier_state *state)
-{
- int opcode; /* current opcode */
- int len; /* for counting instructions, etc. */
- bool superblockend; /* true if no fallthrough to next block */
- instruction *iptr; /* the current instruction */
- basicblock *tbptr; /* temporary for target block */
- bool maythrow; /* true if this instruction may throw */
- s4 i;
- typecheck_result r;
- branch_target_t *table;
- lookup_target_t *lookup;
- jitdata *jd = state->jd;
- exception_entry *ex;
- varinfo constvalue; /* for PUT*CONST */
- constant_FMIref *fieldref;
-
- LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",state->bptr->nr);
- LOGFLUSH;
-
- superblockend = false;
- state->bptr->flags = BBFINISHED;
-
- /* prevent compiler warnings */
-
-
- /* determine the active exception handlers for this block */
- /* XXX could use a faster algorithm with sorted lists or */
- /* something? */
- len = 0;
- for (ex = state->jd->exceptiontable; ex ; ex = ex->down) {
- if ((ex->start->nr <= state->bptr->nr) && (ex->end->nr > state->bptr->nr)) {
- LOG1("active handler L%03d", ex->handler->nr);
- state->handlers[len++] = ex;
- }
- }
- state->handlers[len] = NULL;
-
- /* init variable types at the start of this block */
- typevector_copy_inplace(state->bptr->inlocals, jd->var, state->numlocals);
-
- DOLOG(show_basicblock(jd, state->bptr, SHOW_STACK));
- DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->invars,
- state->bptr->indepth));
- DOLOG(typevector_print(stdout, jd->var, state->numlocals));
- LOGNL; LOGFLUSH;
-
- /* loop over the instructions */
- len = state->bptr->icount;
- state->iptr = state->bptr->iinstr;
- while (--len >= 0) {
- TYPECHECK_COUNT(stat_ins);
-
- iptr = state->iptr;
-
- DOLOG(typevector_print(stdout, jd->var, state->numlocals));
- LOGNL; LOGFLUSH;
- DOLOG(show_icmd(jd, state->iptr, false, SHOW_STACK)); LOGNL; LOGFLUSH;
-
- opcode = iptr->opc;
- maythrow = false;
-
- switch (opcode) {
-
- /* include generated code for ICMDs verification */
-
-#define TYPECHECK_VARIABLESBASED
-#define STATE state
-#define METHOD (state->m)
-#define IPTR iptr
-#define BPTR (state->bptr)
-#include <typecheck-variablesbased-gen.inc>
-#undef STATE
-#undef METHOD
-#undef IPTR
-#undef BPTR
-#undef TYPECHECK_VARIABLESBASED
-
- default:
- LOG1("ICMD %d\n", opcode);
- TYPECHECK_VERIFYERROR_bool("Missing ICMD code during typecheck");
- }
-
- /* reach exception handlers for this instruction */
-
- if (maythrow) {
- TYPECHECK_COUNT(stat_ins_maythrow);
- TYPECHECK_MARK(state->stat_maythrow);
- LOG("reaching exception handlers");
- i = 0;
- while (state->handlers[i]) {
- TYPECHECK_COUNT(stat_handlers_reached);
- if (state->handlers[i]->catchtype.any)
- VAR(state->exinvars)->typeinfo.typeclass = state->handlers[i]->catchtype;
- else
- VAR(state->exinvars)->typeinfo.typeclass.cls = class_java_lang_Throwable;
- if (!typestate_reach(state,
- state->handlers[i]->handler,
- &(state->exinvars), jd->var, 1))
- return false;
- i++;
- }
- }
-
- LOG("\t\tnext instruction");
- state->iptr++;
- } /* while instructions */
-
- LOG("instructions done");
- LOGSTR("RESULT=> ");
- DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->outvars,
- state->bptr->outdepth));
- DOLOG(typevector_print(stdout, jd->var, state->numlocals));
- LOGNL; LOGFLUSH;
-
- /* propagate stack and variables to the following block */
- if (!superblockend) {
- LOG("reaching following block");
- tbptr = state->bptr->next;
- while (tbptr->flags == BBDELETED) {
- tbptr = tbptr->next;
-#ifdef TYPECHECK_DEBUG
- /* this must be checked in parse.c */
- if ((tbptr->nr) >= state->basicblockcount)
- TYPECHECK_VERIFYERROR_bool("Control flow falls off the last block");
-#endif
- }
- if (!typestate_reach(state,tbptr,state->bptr->outvars, jd->var,
- state->bptr->outdepth))
- return false;
- }
-
- /* We may have to restore the types of the instack slots. They
- * have been saved if an <init> call inside the block has
- * modified the instack types. (see INVOKESPECIAL) */
-
- if (state->savedinvars)
- typestate_restore_invars(state);
-
- return true;
-}
-
-
-/****************************************************************************/
-/* typecheck() */
-/* This is the main function of the bytecode verifier. It is called */
-/* directly after analyse_stack. */
-/* */
-/* IN: */
-/* meth.............the method to verify */
-/* cdata............codegendata for the method */
-/* rdata............registerdata for the method */
-/* */
-/* RETURN VALUE: */
-/* true.............successful verification */
-/* false............an exception has been thrown */
-/* */
-/****************************************************************************/
-
-#define MAXPARAMS 255
-
-bool typecheck(jitdata *jd)
-{
- methodinfo *meth;
- codegendata *cd;
- varinfo *savedlocals;
- verifier_state state; /* current state of the verifier */
-
- /* collect statistics */
-
-#ifdef TYPECHECK_STATISTICS
- int count_iterations = 0;
- TYPECHECK_COUNT(stat_typechecked);
- TYPECHECK_COUNT_FREQ(stat_locals,jd->maxlocals,STAT_LOCALS);
- TYPECHECK_COUNT_FREQ(stat_blocks,cdata->method->basicblockcount/10,STAT_BLOCKS);
- TYPECHECK_COUNTIF(cdata->method->exceptiontablelength != 0,stat_methods_with_handlers);
- state.stat_maythrow = false;
-#endif
-
- /* get required compiler data */
-
- meth = jd->m;
- cd = jd->cd;
-
- /* some logging on entry */
-
-
- LOGSTR("\n==============================================================================\n");
- DOLOG( show_method(jd, SHOW_STACK) );
- LOGSTR("\n==============================================================================\n");
- LOGMETHOD("Entering typecheck: ",cd->method);
-
- /* initialize the verifier state */
-
- state.m = meth;
- state.jd = jd;
- state.cd = cd;
- state.basicblockcount = jd->basicblockcount;
- state.basicblocks = jd->basicblocks;
- state.savedindices = NULL;
- state.savedinvars = NULL;
-
- /* check that the basicblock numbers are valid */
-
-#if !defined(NDEBUG)
- jit_check_basicblock_numbers(jd);
-#endif
-
- /* check if this method is an instance initializer method */
-
- state.initmethod = (state.m->name == utf_init);
-
- /* initialize the basic block flags for the following CFG traversal */
-
- typecheck_init_flags(&state, BBFINISHED);
-
- /* number of local variables */
-
- /* In <init> methods we use an extra local variable to indicate whether */
- /* the 'this' reference has been initialized. */
- /* TYPE_VOID...means 'this' has not been initialized, */
- /* TYPE_INT....means 'this' has been initialized. */
-
- state.numlocals = state.jd->localcount;
- state.validlocals = state.numlocals;
- if (state.initmethod)
- state.numlocals++; /* VERIFIER_EXTRA_LOCALS */
-
- DOLOG(
- s4 i;
- s4 t;
- LOG("reverselocalmap:");
- for (i=0; i<state.validlocals; ++i) {
- LOG2(" %i => javaindex %i", i, jd->reverselocalmap[i]);
- });
-
- /* allocate the buffer of active exception handlers */
-
- state.handlers = DMNEW(exception_entry*, state.jd->exceptiontablelength + 1);
-
- /* save local variables */
-
- savedlocals = DMNEW(varinfo, state.numlocals);
- MCOPY(savedlocals, jd->var, varinfo, state.numlocals);
-
- /* initialized local variables of first block */
-
- if (!typecheck_init_locals(&state, true))
- return false;
-
- /* initialize invars of exception handlers */
-
- state.exinvars = state.numlocals;
- VAR(state.exinvars)->type = TYPE_ADR;
- typeinfo_init_classinfo(&(VAR(state.exinvars)->typeinfo),
- class_java_lang_Throwable); /* changed later */
-
- LOG("Exception handler stacks set.\n");
-
- /* loop while there are still blocks to be checked */
- do {
- TYPECHECK_COUNT(count_iterations);
-
- state.repeat = false;
-
- state.bptr = state.basicblocks;
-
- for (; state.bptr; state.bptr = state.bptr->next) {
- LOGSTR1("---- BLOCK %04d, ",state.bptr->nr);
- LOGSTR1("blockflags: %d\n",state.bptr->flags);
- LOGFLUSH;
-
- /* verify reached block */
- if (state.bptr->flags == BBTYPECHECK_REACHED) {
- if (!handle_basic_block(&state))
- return false;
- }
- } /* for blocks */
-
- LOGIF(state.repeat,"state.repeat == true");
- } while (state.repeat);
-
- /* statistics */
-
-#ifdef TYPECHECK_STATISTICS
- LOG1("Typechecker did %4d iterations",count_iterations);
- TYPECHECK_COUNT_FREQ(stat_iterations,count_iterations,STAT_ITERATIONS);
- TYPECHECK_COUNTIF(state.jsrencountered,stat_typechecked_jsr);
- TYPECHECK_COUNTIF(state.stat_maythrow,stat_methods_maythrow);
-#endif
-
- /* reset the flags of blocks we haven't reached */
-
- typecheck_reset_flags(&state);
-
- /* restore locals */
-
- MCOPY(jd->var, savedlocals, varinfo, state.numlocals);
-
- /* everything's ok */
-
- LOGimp("exiting typecheck");
- return true;
-}
-#endif /* ENABLE_VERIFIER */
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/verify/typecheck.c - typechecking (part of bytecode verification)
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+/*
+
+What's the purpose of the `typechecker`?
+----------------------------------------
+
+The typechecker analyses (the intermediate repr. of) the bytecode of
+each method and ensures that for each instruction the values on the
+stack and in local variables are of the correct type whenever the
+instruction is executed.
+
+type checking is a mandatory part of bytecode verification.
+
+
+How does the typechecker work?
+------------------------------
+
+The JVM stack and the local variables are not statically typed, so the
+typechecker has to *infer* the static types of stack slots and local
+variables at each point of the method. The JVM spec imposes a lot of
+restrictions on the bytecode in order to guarantee that this is always
+possible.
+
+Basically the typechecker solves the data flow equations of the method.
+This is done in the usual way for a forward data flow analysis: Starting
+from the entry point of the method the typechecker follows the CFG and
+records the type of each stack slot and local variable at each point[1].
+When two or more control flow paths merge at a point, the union of the
+types for each slot/variable is taken. The algorithm continues to follow
+all possible paths[2] until the recorded types do not change anymore (ie.
+the equations have been solved).
+
+If the solution has been reached and the resulting types are valid for
+all instructions, then type checking terminates with success, otherwise
+an exception is thrown.
+
+
+Why is this code so damn complicated?
+-------------------------------------
+
+Short answer: The devil's in the details.
+
+While the basic operation of the typechecker is no big deal, there are
+many properties of Java bytecode which make type checking hard. Some of
+them are not even addressed in the JVM spec. Some problems and their
+solutions:
+
+*) Finding a good representation of the union of two reference types is
+difficult because of multiple inheritance of interfaces.
+
+ Solution: The typeinfo system can represent such "merged" types by a
+ list of proper subclasses of a class. Example:
+
+ typeclass=java.lang.Object merged={ InterfaceA, InterfaceB }
+
+ represents the result of merging two interface types "InterfaceA"
+ and "InterfaceB".
+
+*) When the code of a method is verified, there may still be unresolved
+references to classes/methods/fields in the code, which we may not force
+to be resolved eagerly. (A similar problem arises because of the special
+checks for protected members.)
+
+ Solution: The typeinfo system knows how to deal with unresolved
+ class references. Whenever a check has to be performed for an
+ unresolved type, the type is annotated with constraints representing
+ the check. Later, when the type is resolved, the constraints are
+ checked. (See the constrain_unresolved_... and the resolve_...
+ methods.)[3]
+
+*) Checks for uninitialized object instances are hard because after the
+invocation of <init> on an uninitialized object *all* slots/variables
+referring to this object (and exactly those slots/variables) must be
+marked as initialized.
+
+ Solution: The JVM spec describes a solution, which has been
+ implemented in this typechecker.
+
+Note that some checks mentioned in the JVM spec are unnecessary[4] and
+not performed by either the reference implementation, or this implementation.
+
+
+--- Footnotes
+
+[1] Actually only the types of slots/variables at the start of each
+basic block are remembered. Within a basic block the algorithm only keeps
+the types of the slots/variables for the "current" instruction which is
+being analysed.
+
+[2] Actually the algorithm iterates through the basic block list until
+there are no more changes. Theoretically it would be wise to sort the
+basic blocks topologically beforehand, but the number of average/max
+iterations observed is so low, that this was not deemed necessary.
+
+[3] This is similar to a method proposed by: Alessandro Coglio et al., A
+Formal Specification of Java Class Loading, Technical Report, Kestrel
+Institute April 2000, revised July 2000
+http://www.kestrel.edu/home/people/coglio/loading.pdf
+An important difference is that Coglio's subtype constraints are checked
+after loading, while our constraints are checked when the field/method
+is accessed for the first time, so we can guarantee lexically correct
+error reporting.
+
+[4] Alessandro Coglio
+ Improving the official specification of Java bytecode verification
+ Proceedings of the 3rd ECOOP Workshop on Formal Techniques for Java Programs
+ June 2001
+ citeseer.ist.psu.edu/article/coglio03improving.html
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "vm/types.h"
+
+#ifdef ENABLE_VERIFIER
+
+#include "mm/memory.h"
+
+#include "native/native.hpp"
+
+#include "toolbox/logging.h"
+
+#include "vm/access.hpp"
+#include "vm/array.hpp"
+#include "vm/jit/builtin.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/global.h"
+#include "vm/globals.hpp"
+#include "vm/loader.hpp"
+#include "vm/options.h"
+#include "vm/primitive.hpp"
+#include "vm/resolve.hpp"
+
+#include "vm/jit/jit.hpp"
+#include "vm/jit/parse.hpp"
+#include "vm/jit/show.hpp"
+
+#include "vm/jit/verify/typecheck-common.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/****************************************************************************/
+/* MACROS FOR VARIABLE TYPE CHECKING */
+/****************************************************************************/
+
+#define TYPECHECK_CHECK_TYPE(i,tp,msg) \
+ do { \
+ if (VAR(i)->type != (tp)) { \
+ exceptions_throw_verifyerror(state->m, (msg)); \
+ return false; \
+ } \
+ } while (0)
+
+#define TYPECHECK_INT(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_INT,"Expected to find integer value")
+#define TYPECHECK_LNG(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_LNG,"Expected to find long value")
+#define TYPECHECK_FLT(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_FLT,"Expected to find float value")
+#define TYPECHECK_DBL(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_DBL,"Expected to find double value")
+#define TYPECHECK_ADR(i) \
+ TYPECHECK_CHECK_TYPE(i,TYPE_ADR,"Expected to find object value")
+
+#define TYPECHECK_INT_OP(o) TYPECHECK_INT((o).varindex)
+#define TYPECHECK_LNG_OP(o) TYPECHECK_LNG((o).varindex)
+#define TYPECHECK_FLT_OP(o) TYPECHECK_FLT((o).varindex)
+#define TYPECHECK_DBL_OP(o) TYPECHECK_DBL((o).varindex)
+#define TYPECHECK_ADR_OP(o) TYPECHECK_ADR((o).varindex)
+
+
+/* typestate_save_invars *******************************************************
+
+ Save the invars of the current basic block in the space reserved by
+ parse.
+
+ This function must be called before an instruction modifies a variable
+ that is an invar of the current block. In such cases the invars of the
+ block must be saved, and restored at the end of the analysis of this
+ basic block, so that the invars again reflect the *input* to this basic
+ block (and do not randomly contain types that appear within the block).
+
+ IN:
+ state............current state of the verifier
+
+*******************************************************************************/
+
+static void
+typestate_save_invars(verifier_state *state)
+{
+ s4 i, index;
+ s4 *pindex;
+
+ LOG("saving invars");
+
+ if (!state->savedindices) {
+ LOG("allocating savedindices buffer");
+ pindex = (s4*) DumpMemory::allocate(sizeof(s4) * state->m->maxstack);
+ state->savedindices = pindex;
+ index = state->numlocals + VERIFIER_EXTRA_VARS;
+ for (i=0; i<state->m->maxstack; ++i)
+ *pindex++ = index++;
+ }
+
+ /* save types */
+
+ typecheck_copy_types(state, state->bptr->invars, state->savedindices,
+ state->bptr->indepth);
+
+ /* set the invars of the block to the saved variables */
+ /* and remember the original invars */
+
+ state->savedinvars = state->bptr->invars;
+ state->bptr->invars = state->savedindices;
+}
+
+
+/* typestate_restore_invars ***************************************************
+
+ Restore the invars of the current basic block that have been previously
+ saved by `typestate_save_invars`.
+
+ IN:
+ state............current state of the verifier
+
+*******************************************************************************/
+
+static void
+typestate_restore_invars(verifier_state *state)
+{
+ TYPECHECK_COUNT(stat_savedstack);
+ LOG("restoring saved invars");
+
+ /* restore the invars pointer */
+
+ state->bptr->invars = state->savedinvars;
+
+ /* copy the types back */
+
+ typecheck_copy_types(state, state->savedindices, state->bptr->invars,
+ state->bptr->indepth);
+
+ /* mark that there are no saved invars currently */
+
+ state->savedinvars = NULL;
+}
+
+
+/* handle_fieldaccess **********************************************************
+
+ Verify an ICMD_{GET,PUT}{STATIC,FIELD}(CONST)?
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_fieldaccess(verifier_state *state,
+ varinfo *instance,
+ varinfo *value)
+{
+ jitdata *jd;
+
+ jd = state->jd;
+
+#define TYPECHECK_VARIABLESBASED
+#define EXCEPTION do { return false; } while (0)
+#define VERIFY_ERROR(msg) TYPECHECK_VERIFYERROR_bool(msg)
+#include <typecheck-fields.inc>
+#undef EXCEPTION
+#undef VERIFY_ERROR
+#undef TYPECHECK_VARIABLESBASED
+
+ return true;
+}
+
+
+/* handle_invocation ***********************************************************
+
+ Verify an ICMD_INVOKE* instruction.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_invocation(verifier_state *state)
+{
+ jitdata *jd;
+ varinfo *dv; /* output variable of current instruction */
+
+ jd = state->jd;
+ dv = VAROP(state->iptr->dst);
+
+#define TYPECHECK_VARIABLESBASED
+#define OP1 VAR(state->iptr->sx.s23.s2.args[0])
+#include <typecheck-invoke.inc>
+#undef OP1
+#undef TYPECHECK_VARIABLESBASED
+
+ return true;
+}
+
+
+/* handle_builtin **************************************************************
+
+ Verify the call of a builtin method.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_builtin(verifier_state *state)
+{
+ jitdata *jd;
+ varinfo *dv; /* output variable of current instruction */
+
+ jd = state->jd;
+ dv = VAROP(state->iptr->dst);
+
+#define TYPECHECK_VARIABLESBASED
+#define OP1 state->iptr->sx.s23.s2.args[0]
+#include <typecheck-builtins.inc>
+#undef OP1
+#undef TYPECHECK_VARIABLESBASED
+
+ return true;
+}
+
+/* handle_multianewarray *******************************************************
+
+ Verify a MULTIANEWARRAY instruction.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_multianewarray(verifier_state *state)
+{
+ jitdata *jd;
+ varinfo *dv; /* output variable of current instruction */
+
+ jd = state->jd;
+ dv = VAROP(state->iptr->dst);
+
+#define TYPECHECK_VARIABLESBASED
+#define VERIFY_ERROR(msg) TYPECHECK_VERIFYERROR_bool(msg)
+#include <typecheck-multianewarray.inc>
+#undef VERIFY_ERROR
+#undef TYPECHECK_VARIABLESBASED
+
+ return true;
+}
+
+/* typecheck_invalidate_locals *************************************************
+
+ Invalidate locals that are overwritten by writing to the given local.
+
+ IN:
+ state............the current state of the verifier
+ index............the index of the local that is written
+ twoword..........true, if a two-word type is written
+
+*******************************************************************************/
+
+static void typecheck_invalidate_locals(verifier_state *state, s4 index, bool twoword)
+{
+ s4 javaindex;
+ s4 t;
+ s4 varindex;
+ jitdata *jd = state->jd;
+ s4 *localmap = jd->local_map;
+ varinfo *vars = jd->var;
+
+ javaindex = jd->reverselocalmap[index];
+
+ /* invalidate locals of two-word type at index javaindex-1 */
+
+ if (javaindex > 0) {
+ localmap += 5 * (javaindex-1);
+ for (t=0; t<5; ++t) {
+ varindex = *localmap++;
+ if (varindex >= 0 && IS_2_WORD_TYPE(vars[varindex].type)) {
+ LOG1("invalidate local %d", varindex);
+ vars[varindex].type = TYPE_VOID;
+ }
+ }
+ }
+ else {
+ localmap += 5 * javaindex;
+ }
+
+ /* invalidate locals at index javaindex */
+
+ for (t=0; t<5; ++t) {
+ varindex = *localmap++;
+ if (varindex >= 0) {
+ LOG1("invalidate local %d", varindex);
+ vars[varindex].type = TYPE_VOID;
+ }
+ }
+
+ /* if a two-word type is written, invalidate locals at index javaindex+1 */
+
+ if (twoword) {
+ for (t=0; t<5; ++t) {
+ varindex = *localmap++;
+ if (varindex >= 0) {
+ LOG1("invalidate local %d", varindex);
+ vars[varindex].type = TYPE_VOID;
+ }
+ }
+ }
+}
+
+
+/* macros used by the generated code ******************************************/
+
+#define EXCEPTION do { return false; } while (0)
+#define VERIFY_ERROR(msg) TYPECHECK_VERIFYERROR_bool(msg)
+
+#define CHECK_LOCAL_TYPE(index, t) \
+ do { \
+ if (!typevector_checktype(jd->var, (index), (t))) \
+ VERIFY_ERROR("Local variable type mismatch"); \
+ } while (0)
+
+#define STORE_LOCAL(t, index) \
+ do { \
+ s4 temp_t = (t); \
+ typecheck_invalidate_locals(state, (index), false); \
+ typevector_store(jd->var, (index), (temp_t), NULL); \
+ } while (0)
+
+#define STORE_LOCAL_2_WORD(t, index) \
+ do { \
+ s4 temp_t = (t); \
+ typecheck_invalidate_locals(state, (index), true); \
+ typevector_store(jd->var, (index), (temp_t), NULL); \
+ } while (0)
+
+#define REACH_BLOCK(target) \
+ do { \
+ if (!typestate_reach(state, (target), \
+ state->bptr->outvars, jd->var, \
+ state->bptr->outdepth)) \
+ return false; \
+ } while (0)
+
+#define REACH(target) REACH_BLOCK((target).block)
+
+
+/* handle_basic_block **********************************************************
+
+ Perform bytecode verification of a basic block.
+
+ IN:
+ state............the current state of the verifier
+
+ RETURN VALUE:
+ true.............successful verification,
+ false............an exception has been thrown.
+
+*******************************************************************************/
+
+static bool
+handle_basic_block(verifier_state *state)
+{
+ int opcode; /* current opcode */
+ int len; /* for counting instructions, etc. */
+ bool superblockend; /* true if no fallthrough to next block */
+ instruction *iptr; /* the current instruction */
+ basicblock *tbptr; /* temporary for target block */
+ bool maythrow; /* true if this instruction may throw */
+ s4 i;
+ typecheck_result r;
+ branch_target_t *table;
+ lookup_target_t *lookup;
+ jitdata *jd = state->jd;
+ exception_entry *ex;
+ varinfo constvalue; /* for PUT*CONST */
+ constant_FMIref *fieldref;
+
+ LOGSTR1("\n---- BLOCK %04d ------------------------------------------------\n",state->bptr->nr);
+ LOGFLUSH;
+
+ superblockend = false;
+ state->bptr->flags = BBFINISHED;
+
+ /* prevent compiler warnings */
+
+
+ /* determine the active exception handlers for this block */
+ /* XXX could use a faster algorithm with sorted lists or */
+ /* something? */
+ len = 0;
+ for (ex = state->jd->exceptiontable; ex ; ex = ex->down) {
+ if ((ex->start->nr <= state->bptr->nr) && (ex->end->nr > state->bptr->nr)) {
+ LOG1("active handler L%03d", ex->handler->nr);
+ state->handlers[len++] = ex;
+ }
+ }
+ state->handlers[len] = NULL;
+
+ /* init variable types at the start of this block */
+ typevector_copy_inplace(state->bptr->inlocals, jd->var, state->numlocals);
+
+ DOLOG(show_basicblock(jd, state->bptr, SHOW_STACK));
+ DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->invars,
+ state->bptr->indepth));
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
+ LOGNL; LOGFLUSH;
+
+ /* loop over the instructions */
+ len = state->bptr->icount;
+ state->iptr = state->bptr->iinstr;
+ while (--len >= 0) {
+ TYPECHECK_COUNT(stat_ins);
+
+ iptr = state->iptr;
+
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
+ LOGNL; LOGFLUSH;
+ DOLOG(show_icmd(jd, state->iptr, false, SHOW_STACK)); LOGNL; LOGFLUSH;
+
+ opcode = iptr->opc;
+ maythrow = false;
+
+ switch (opcode) {
+
+ /* include generated code for ICMDs verification */
+
+#define TYPECHECK_VARIABLESBASED
+#define STATE state
+#define METHOD (state->m)
+#define IPTR iptr
+#define BPTR (state->bptr)
+#include <typecheck-variablesbased-gen.inc>
+#undef STATE
+#undef METHOD
+#undef IPTR
+#undef BPTR
+#undef TYPECHECK_VARIABLESBASED
+
+ default:
+ LOG1("ICMD %d\n", opcode);
+ TYPECHECK_VERIFYERROR_bool("Missing ICMD code during typecheck");
+ }
+
+ /* reach exception handlers for this instruction */
+
+ if (maythrow) {
+ TYPECHECK_COUNT(stat_ins_maythrow);
+ TYPECHECK_MARK(state->stat_maythrow);
+ LOG("reaching exception handlers");
+ i = 0;
+ while (state->handlers[i]) {
+ TYPECHECK_COUNT(stat_handlers_reached);
+ if (state->handlers[i]->catchtype.any)
+ VAR(state->exinvars)->typeinfo.typeclass = state->handlers[i]->catchtype;
+ else
+ VAR(state->exinvars)->typeinfo.typeclass.cls = class_java_lang_Throwable;
+ if (!typestate_reach(state,
+ state->handlers[i]->handler,
+ &(state->exinvars), jd->var, 1))
+ return false;
+ i++;
+ }
+ }
+
+ LOG("\t\tnext instruction");
+ state->iptr++;
+ } /* while instructions */
+
+ LOG("instructions done");
+ LOGSTR("RESULT=> ");
+ DOLOG(typecheck_print_vararray(stdout, jd, state->bptr->outvars,
+ state->bptr->outdepth));
+ DOLOG(typevector_print(stdout, jd->var, state->numlocals));
+ LOGNL; LOGFLUSH;
+
+ /* propagate stack and variables to the following block */
+ if (!superblockend) {
+ LOG("reaching following block");
+ tbptr = state->bptr->next;
+ while (tbptr->flags == BBDELETED) {
+ tbptr = tbptr->next;
+#ifdef TYPECHECK_DEBUG
+ /* this must be checked in parse.c */
+ if ((tbptr->nr) >= state->basicblockcount)
+ TYPECHECK_VERIFYERROR_bool("Control flow falls off the last block");
+#endif
+ }
+ if (!typestate_reach(state,tbptr,state->bptr->outvars, jd->var,
+ state->bptr->outdepth))
+ return false;
+ }
+
+ /* We may have to restore the types of the instack slots. They
+ * have been saved if an <init> call inside the block has
+ * modified the instack types. (see INVOKESPECIAL) */
+
+ if (state->savedinvars)
+ typestate_restore_invars(state);
+
+ return true;
+}
+
+
+/****************************************************************************/
+/* typecheck() */
+/* This is the main function of the bytecode verifier. It is called */
+/* directly after analyse_stack. */
+/* */
+/* IN: */
+/* meth.............the method to verify */
+/* cdata............codegendata for the method */
+/* rdata............registerdata for the method */
+/* */
+/* RETURN VALUE: */
+/* true.............successful verification */
+/* false............an exception has been thrown */
+/* */
+/****************************************************************************/
+
+#define MAXPARAMS 255
+
+bool typecheck(jitdata *jd)
+{
+ methodinfo *meth;
+ codegendata *cd;
+ varinfo *savedlocals;
+ verifier_state state; /* current state of the verifier */
+
+ /* collect statistics */
+
+#ifdef TYPECHECK_STATISTICS
+ int count_iterations = 0;
+ TYPECHECK_COUNT(stat_typechecked);
+ TYPECHECK_COUNT_FREQ(stat_locals,jd->maxlocals,STAT_LOCALS);
+ TYPECHECK_COUNT_FREQ(stat_blocks,cdata->method->basicblockcount/10,STAT_BLOCKS);
+ TYPECHECK_COUNTIF(cdata->method->exceptiontablelength != 0,stat_methods_with_handlers);
+ state.stat_maythrow = false;
+#endif
+
+ /* get required compiler data */
+
+ meth = jd->m;
+ cd = jd->cd;
+
+ /* some logging on entry */
+
+
+ LOGSTR("\n==============================================================================\n");
+ DOLOG( show_method(jd, SHOW_STACK) );
+ LOGSTR("\n==============================================================================\n");
+ LOGMETHOD("Entering typecheck: ",cd->method);
+
+ /* initialize the verifier state */
+
+ state.m = meth;
+ state.jd = jd;
+ state.cd = cd;
+ state.basicblockcount = jd->basicblockcount;
+ state.basicblocks = jd->basicblocks;
+ state.savedindices = NULL;
+ state.savedinvars = NULL;
+
+ /* check that the basicblock numbers are valid */
+
+#if !defined(NDEBUG)
+ jit_check_basicblock_numbers(jd);
+#endif
+
+ /* check if this method is an instance initializer method */
+
+ state.initmethod = (state.m->name == utf_init);
+
+ /* initialize the basic block flags for the following CFG traversal */
+
+ typecheck_init_flags(&state, BBFINISHED);
+
+ /* number of local variables */
+
+ /* In <init> methods we use an extra local variable to indicate whether */
+ /* the 'this' reference has been initialized. */
+ /* TYPE_VOID...means 'this' has not been initialized, */
+ /* TYPE_INT....means 'this' has been initialized. */
+
+ state.numlocals = state.jd->localcount;
+ state.validlocals = state.numlocals;
+ if (state.initmethod)
+ state.numlocals++; /* VERIFIER_EXTRA_LOCALS */
+
+ DOLOG(
+ s4 i;
+ s4 t;
+ LOG("reverselocalmap:");
+ for (i=0; i<state.validlocals; ++i) {
+ LOG2(" %i => javaindex %i", i, jd->reverselocalmap[i]);
+ });
+
+ /* allocate the buffer of active exception handlers */
+
+ state.handlers = (exception_entry**) DumpMemory::allocate(sizeof(exception_entry*) * (state.jd->exceptiontablelength + 1));
+
+ /* save local variables */
+
+ savedlocals = (varinfo*) DumpMemory::allocate(sizeof(varinfo) * state.numlocals);
+ MCOPY(savedlocals, jd->var, varinfo, state.numlocals);
+
+ /* initialized local variables of first block */
+
+ if (!typecheck_init_locals(&state, true))
+ return false;
+
+ /* initialize invars of exception handlers */
+
+ state.exinvars = state.numlocals;
+ VAR(state.exinvars)->type = TYPE_ADR;
+ typeinfo_init_classinfo(&(VAR(state.exinvars)->typeinfo),
+ class_java_lang_Throwable); /* changed later */
+
+ LOG("Exception handler stacks set.\n");
+
+ /* loop while there are still blocks to be checked */
+ do {
+ TYPECHECK_COUNT(count_iterations);
+
+ state.repeat = false;
+
+ state.bptr = state.basicblocks;
+
+ for (; state.bptr; state.bptr = state.bptr->next) {
+ LOGSTR1("---- BLOCK %04d, ",state.bptr->nr);
+ LOGSTR1("blockflags: %d\n",state.bptr->flags);
+ LOGFLUSH;
+
+ /* verify reached block */
+ if (state.bptr->flags == BBTYPECHECK_REACHED) {
+ if (!handle_basic_block(&state))
+ return false;
+ }
+ } /* for blocks */
+
+ LOGIF(state.repeat,"state.repeat == true");
+ } while (state.repeat);
+
+ /* statistics */
+
+#ifdef TYPECHECK_STATISTICS
+ LOG1("Typechecker did %4d iterations",count_iterations);
+ TYPECHECK_COUNT_FREQ(stat_iterations,count_iterations,STAT_ITERATIONS);
+ TYPECHECK_COUNTIF(state.jsrencountered,stat_typechecked_jsr);
+ TYPECHECK_COUNTIF(state.stat_maythrow,stat_methods_maythrow);
+#endif
+
+ /* reset the flags of blocks we haven't reached */
+
+ typecheck_reset_flags(&state);
+
+ /* restore locals */
+
+ MCOPY(jd->var, savedlocals, varinfo, state.numlocals);
+
+ /* everything's ok */
+
+ LOGimp("exiting typecheck");
+ return true;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* ENABLE_VERIFIER */
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typecheck.h - type checking header
-
- Copyright (C) 1996-2005, 2006, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _TYPECHECK_H
-#define _TYPECHECK_H
-
-#include "config.h"
-
-#include "vm/global.h"
-#include "vm/jit/jit.hpp"
-
-
-/* function prototypes ********************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#if defined(ENABLE_VERIFIER)
-bool typecheck(jitdata *jd);
-bool typecheck_stackbased(jitdata *jd);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _TYPECHECK_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
--- /dev/null
+/* src/vm/jit/verify/typecheck.h - type checking header
+
+ Copyright (C) 1996-2005, 2006, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _TYPECHECK_H
+#define _TYPECHECK_H
+
+#include "config.h"
+
+#include "vm/global.h"
+#include "vm/jit/jit.hpp"
+
+
+/* function prototypes ********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(ENABLE_VERIFIER)
+bool typecheck(jitdata *jd);
+bool typecheck_stackbased(jitdata *jd);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TYPECHECK_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typeinfo.c - type system used by the type checker
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <string.h>
-
-#include "mm/memory.h"
-
-#include "toolbox/logging.h"
-
-#include "vm/array.hpp"
-#include "vm/class.hpp"
-#include "vm/descriptor.h"
-#include "vm/exceptions.hpp"
-#include "vm/globals.hpp"
-#include "vm/loader.hpp"
-#include "vm/primitive.hpp"
-#include "vm/resolve.hpp"
-
-#include "vm/jit/jit.hpp"
-#include "vm/jit/verify/typeinfo.h"
-
-
-/* check if a linked class is an array class. Only use for linked classes! */
-#define CLASSINFO_IS_ARRAY(clsinfo) ((clsinfo)->vftbl->arraydesc != NULL)
-
-/* check if a linked class implements the interface with the given index */
-#define CLASSINFO_IMPLEMENTS_INTERFACE(cls,index) \
- ( ((index) < (cls)->vftbl->interfacetablelength) \
- && ( (cls)->vftbl->interfacetable[-(index)] != NULL ) )
-
-/******************************************************************************/
-/* DEBUG HELPERS */
-/******************************************************************************/
-
-#ifdef TYPEINFO_DEBUG
-#define TYPEINFO_ASSERT(cond) assert(cond)
-#else
-#define TYPEINFO_ASSERT(cond)
-#endif
-
-/**********************************************************************/
-/* TYPEVECTOR FUNCTIONS */
-/**********************************************************************/
-
-#if defined(ENABLE_VERIFIER)
-
-/* typevector_copy *************************************************************
-
- Return a copy of the given typevector.
-
- IN:
- src..............typevector set to copy, must be != NULL
- size.............number of elements per typevector
-
- RETURN VALUE:
- a pointer to the new typevector set
-
-*******************************************************************************/
-
-varinfo *
-typevector_copy(varinfo *src, int size)
-{
- varinfo *dst;
-
- TYPEINFO_ASSERT(src);
-
- dst = DNEW_TYPEVECTOR(size);
- memcpy(dst,src,TYPEVECTOR_SIZE(size));
-
- return dst;
-}
-
-/* typevector_copy_inplace *****************************************************
-
- Copy a typevector to a given destination.
-
- IN:
- src..............typevector to copy, must be != NULL
- dst..............destination to write the copy to
- size.............number of elements per typevector
-
-*******************************************************************************/
-
-void
-typevector_copy_inplace(varinfo *src,varinfo *dst,int size)
-{
- memcpy(dst,src,TYPEVECTOR_SIZE(size));
-}
-
-/* typevector_checktype ********************************************************
-
- Check if the typevector contains a given type at a given index.
-
- IN:
- vec..............typevector set, must be != NULL
- index............index of component to check
- type.............TYPE_* constant to check against
-
- RETURN VALUE:
- true if the typevector contains TYPE at INDEX,
- false otherwise
-
-*******************************************************************************/
-
-bool
-typevector_checktype(varinfo *vec,int index,int type)
-{
- TYPEINFO_ASSERT(vec);
-
- return vec[index].type == type;
-}
-
-/* typevector_checkreference ***************************************************
-
- Check if the typevector contains a reference at a given index.
-
- IN:
- vec..............typevector, must be != NULL
- index............index of component to check
-
- RETURN VALUE:
- true if the typevector contains a reference at INDEX,
- false otherwise
-
-*******************************************************************************/
-
-bool
-typevector_checkreference(varinfo *vec, int index)
-{
- TYPEINFO_ASSERT(vec);
- return TYPEDESC_IS_REFERENCE(vec[index]);
-}
-
-/* typevectorset_checkretaddr **************************************************
-
- Check if the typevectors contains a returnAddress at a given index.
-
- IN:
- vec..............typevector, must be != NULL
- index............index of component to check
-
- RETURN VALUE:
- true if the typevector contains a returnAddress at INDEX,
- false otherwise
-
-*******************************************************************************/
-
-bool
-typevector_checkretaddr(varinfo *vec,int index)
-{
- TYPEINFO_ASSERT(vec);
- return TYPEDESC_IS_RETURNADDRESS(vec[index]);
-}
-
-/* typevector_store ************************************************************
-
- Store a type at a given index in the typevector.
-
- IN:
- vec..............typevector set, must be != NULL
- index............index of component to set
- type.............TYPE_* constant of type to set
- info.............typeinfo of type to set, may be NULL,
- if TYPE != TYPE_ADR
-
-*******************************************************************************/
-
-void
-typevector_store(varinfo *vec,int index,int type,typeinfo_t *info)
-{
- TYPEINFO_ASSERT(vec);
-
- vec[index].type = type;
- if (info)
- TYPEINFO_COPY(*info,vec[index].typeinfo);
-}
-
-/* typevector_store_retaddr ****************************************************
-
- Store a returnAddress type at a given index in the typevector.
-
- IN:
- vec..............typevector set, must be != NULL
- index............index of component to set
- info.............typeinfo of the returnAddress.
-
-*******************************************************************************/
-
-void
-typevector_store_retaddr(varinfo *vec,int index,typeinfo_t *info)
-{
- TYPEINFO_ASSERT(vec);
- TYPEINFO_ASSERT(TYPEINFO_IS_PRIMITIVE(*info));
-
- vec[index].type = TYPE_ADR;
- TYPEINFO_INIT_RETURNADDRESS(vec[index].typeinfo,
- TYPEINFO_RETURNADDRESS(*info));
-}
-
-/* typevector_init_object ******************************************************
-
- Replace all uninitialized object types in the typevector set which were
- created by the given instruction by initialized object types.
-
- IN:
- set..............typevector set
- ins..............instruction which created the uninitialized object type
- initclass........class of the initialized object type to set
- size.............number of elements per typevector
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
- XXX maybe we should do the lazy resolving before calling this function
-
-*******************************************************************************/
-
-bool
-typevector_init_object(varinfo *set,void *ins,
- classref_or_classinfo initclass,
- int size)
-{
- int i;
-
- for (i=0; i<size; ++i) {
- if (set[i].type == TYPE_ADR
- && TYPEINFO_IS_NEWOBJECT(set[i].typeinfo)
- && TYPEINFO_NEWOBJECT_INSTRUCTION(set[i].typeinfo) == ins)
- {
- if (!typeinfo_init_class(&(set[i].typeinfo),initclass))
- return false;
- }
- }
- return true;
-}
-
-/* typevector_merge ************************************************************
-
- Merge a typevector with another one.
- The given typevectors must have the same number of components.
-
- IN:
- m................method for exception messages
- dst..............the first typevector
- y................the second typevector
- size.............number of elements per typevector
-
- OUT:
- *dst.............the resulting typevector
-
- RETURN VALUE:
- typecheck_TRUE...dst has been modified
- typecheck_FALSE..dst has not been modified
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-typecheck_result
-typevector_merge(methodinfo *m,varinfo *dst,varinfo *y,int size)
-{
- bool changed = false;
- typecheck_result r;
-
- varinfo *a = dst;
- varinfo *b = y;
- while (size--) {
- if (a->type != TYPE_VOID && a->type != b->type) {
- a->type = TYPE_VOID;
- changed = true;
- }
- else if (a->type == TYPE_ADR) {
- if (TYPEINFO_IS_PRIMITIVE(a->typeinfo)) {
- /* 'a' is a returnAddress */
- if (!TYPEINFO_IS_PRIMITIVE(b->typeinfo)
- || (TYPEINFO_RETURNADDRESS(a->typeinfo)
- != TYPEINFO_RETURNADDRESS(b->typeinfo)))
- {
- a->type = TYPE_VOID;
- changed = true;
- }
- }
- else {
- /* 'a' is a reference */
- if (TYPEINFO_IS_PRIMITIVE(b->typeinfo)) {
- a->type = TYPE_VOID;
- changed = true;
- }
- else {
- /* two reference types are merged. There cannot be */
- /* a merge error. In the worst case we get j.l.O. */
- r = typeinfo_merge(m,&(a->typeinfo),&(b->typeinfo));
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
- }
- }
- }
- a++;
- b++;
- }
- return changed;
-}
-
-/**********************************************************************/
-/* READ-ONLY FUNCTIONS */
-/* The following functions don't change typeinfo data. */
-/**********************************************************************/
-
-/* typeinfo_is_array ***********************************************************
-
- Check whether a typeinfo describes an array type.
-
- IN:
- info.............the typeinfo, must be != NULL
-
- RETURN VALUE:
- true if INFO describes an array type.
-
-*******************************************************************************/
-
-bool
-typeinfo_is_array(typeinfo_t *info)
-{
- TYPEINFO_ASSERT(info);
- return TYPEINFO_IS_ARRAY(*info);
-}
-
-/* typeinfo_is_primitive_array *************************************************
-
- Check whether a typeinfo describes a primitive array type.
-
- IN:
- info.............the typeinfo, must be != NULL
-
- RETURN VALUE:
- true if INFO describes an array of a primitive type.
-
-*******************************************************************************/
-
-bool
-typeinfo_is_primitive_array(typeinfo_t *info,int arraytype)
-{
- TYPEINFO_ASSERT(info);
- return TYPEINFO_IS_PRIMITIVE_ARRAY(*info,arraytype);
-}
-
-/* typeinfo_is_array_of_refs ***************************************************
-
- Check whether a typeinfo describes an array of references type.
-
- IN:
- info.............the typeinfo, must be != NULL
-
- RETURN VALUE:
- true if INFO describes an array of a refrence type.
-
-*******************************************************************************/
-
-bool
-typeinfo_is_array_of_refs(typeinfo_t *info)
-{
- TYPEINFO_ASSERT(info);
- return TYPEINFO_IS_ARRAY_OF_REFS(*info);
-}
-
-/* interface_extends_interface *************************************************
-
- Check if a resolved interface extends a given resolved interface.
-
- IN:
- cls..............the interface, must be linked
- interf...........the interface to check against
-
- RETURN VALUE:
- true.............CLS extends INTERF
- false............CLS does not extend INTERF
-
-*******************************************************************************/
-
-static bool
-interface_extends_interface(classinfo *cls,classinfo *interf)
-{
- int i;
-
- TYPEINFO_ASSERT(cls);
- TYPEINFO_ASSERT(interf);
- TYPEINFO_ASSERT((interf->flags & ACC_INTERFACE) != 0);
- TYPEINFO_ASSERT((cls->flags & ACC_INTERFACE) != 0);
- TYPEINFO_ASSERT(cls->state & CLASS_LINKED);
-
- /* first check direct superinterfaces */
- for (i=0; i<cls->interfacescount; ++i) {
- if (cls->interfaces[i] == interf)
- return true;
- }
-
- /* check indirect superinterfaces */
- for (i=0; i<cls->interfacescount; ++i) {
- if (interface_extends_interface(cls->interfaces[i],interf))
- return true;
- }
-
- return false;
-}
-
-/* classinfo_implements_interface **********************************************
-
- Check if a resolved class implements a given resolved interface.
-
- IN:
- cls..............the class
- interf...........the interface
-
- RETURN VALUE:
- typecheck_TRUE...CLS implements INTERF
- typecheck_FALSE..CLS does not implement INTERF
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-static typecheck_result
-classinfo_implements_interface(classinfo *cls,classinfo *interf)
-{
- TYPEINFO_ASSERT(cls);
- TYPEINFO_ASSERT(interf);
- TYPEINFO_ASSERT((interf->flags & ACC_INTERFACE) != 0);
-
- if (!(cls->state & CLASS_LINKED))
- if (!link_class(cls))
- return typecheck_FAIL;
-
- if (cls->flags & ACC_INTERFACE) {
- /* cls is an interface */
- if (cls == interf)
- return typecheck_TRUE;
-
- /* check superinterfaces */
- return interface_extends_interface(cls,interf);
- }
-
- TYPEINFO_ASSERT(cls->state & CLASS_LINKED);
- return CLASSINFO_IMPLEMENTS_INTERFACE(cls,interf->index);
-}
-
-/* mergedlist_implements_interface *********************************************
-
- Check if all the classes in a given merged list implement a given resolved
- interface.
-
- IN:
- merged...........the list of merged class types
- interf...........the interface to check against
-
- RETURN VALUE:
- typecheck_TRUE...all classes implement INTERF
- typecheck_FALSE..there is at least one class that does not implement
- INTERF
- typecheck_MAYBE..check cannot be performed now because of unresolved
- classes
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-static typecheck_result
-mergedlist_implements_interface(typeinfo_mergedlist_t *merged,
- classinfo *interf)
-{
- int i;
- classref_or_classinfo *mlist;
- typecheck_result r;
-
- TYPEINFO_ASSERT(interf);
- TYPEINFO_ASSERT((interf->flags & ACC_INTERFACE) != 0);
-
- /* Check if there is an non-empty mergedlist. */
- if (!merged)
- return typecheck_FALSE;
-
- /* If all classinfos in the (non-empty) merged array implement the
- * interface return true, otherwise false.
- */
- mlist = merged->list;
- i = merged->count;
- while (i--) {
- if (IS_CLASSREF(*mlist)) {
- return typecheck_MAYBE;
- }
- r = classinfo_implements_interface((mlist++)->cls,interf);
- if (r != typecheck_TRUE)
- return r;
- }
- return typecheck_TRUE;
-}
-
-/* merged_implements_interface *************************************************
-
- Check if a possible merged type implements a given resolved interface
- interface.
-
- IN:
- typeclass........(common) class of the (merged) type
- merged...........the list of merged class types
- interf...........the interface to check against
-
- RETURN VALUE:
- typecheck_TRUE...the type implement INTERF
- typecheck_FALSE..the type does not implement INTERF
- typecheck_MAYBE..check cannot be performed now because of unresolved
- classes
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-static typecheck_result
-merged_implements_interface(classinfo *typeclass,typeinfo_mergedlist_t *merged,
- classinfo *interf)
-{
- typecheck_result r;
-
- /* primitive types don't support interfaces. */
- if (!typeclass)
- return typecheck_FALSE;
-
- /* the null type can be cast to any interface type. */
- if (typeclass == pseudo_class_Null)
- return typecheck_TRUE;
-
- /* check if typeclass implements the interface. */
- r = classinfo_implements_interface(typeclass,interf);
- if (r != typecheck_FALSE)
- return r;
-
- /* check the mergedlist */
- if (!merged)
- return typecheck_FALSE;
- return mergedlist_implements_interface(merged,interf);
-}
-
-/* merged_is_subclass **********************************************************
-
- Check if a possible merged type is a subclass of a given class.
- A merged type is a subclass of a class C if all types in the merged list
- are subclasses of C. A sufficient condition for this is that the
- common type of the merged type is a subclass of C.
-
- IN:
- typeclass........(common) class of the (merged) type
- MUST be a loaded and linked class
- merged...........the list of merged class types
- cls..............the class to theck against
-
- RETURN VALUE:
- typecheck_TRUE...the type is a subclass of CLS
- typecheck_FALSE..the type is not a subclass of CLS
- typecheck_MAYBE..check cannot be performed now because of unresolved
- classes
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-static typecheck_result
-merged_is_subclass(classinfo *typeclass,typeinfo_mergedlist_t *merged,
- classinfo *cls)
-{
- int i;
- classref_or_classinfo *mlist;
-
- TYPEINFO_ASSERT(cls);
-
- /* primitive types aren't subclasses of anything. */
- if (!typeclass)
- return typecheck_FALSE;
-
- /* the null type can be cast to any reference type. */
- if (typeclass == pseudo_class_Null)
- return typecheck_TRUE;
-
- TYPEINFO_ASSERT(typeclass->state & CLASS_LOADED);
- TYPEINFO_ASSERT(typeclass->state & CLASS_LINKED);
-
- /* check if the common typeclass is a subclass of CLS. */
- if (class_issubclass(typeclass,cls))
- return typecheck_TRUE;
-
- /* check the mergedlist */
- if (!merged)
- return typecheck_FALSE;
- /* If all classinfos in the (non-empty) merged list are subclasses
- * of CLS, return true, otherwise false.
- * If there is at least one unresolved type in the list,
- * return typecheck_MAYBE.
- */
- mlist = merged->list;
- i = merged->count;
- while (i--) {
- if (IS_CLASSREF(*mlist)) {
- return typecheck_MAYBE;
- }
- if (!(mlist->cls->state & CLASS_LINKED))
- if (!link_class(mlist->cls))
- return typecheck_FAIL;
- if (!class_issubclass(mlist->cls,cls))
- return typecheck_FALSE;
- mlist++;
- }
- return typecheck_TRUE;
-}
-
-/* typeinfo_is_assignable_to_class *********************************************
-
- Check if a type is assignable to a given class type.
-
- IN:
- value............the type of the value
- dest.............the type of the destination
-
- RETURN VALUE:
- typecheck_TRUE...the type is assignable
- typecheck_FALSE..the type is not assignable
- typecheck_MAYBE..check cannot be performed now because of unresolved
- classes
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-typecheck_result
-typeinfo_is_assignable_to_class(typeinfo_t *value,classref_or_classinfo dest)
-{
- classref_or_classinfo c;
- classinfo *cls;
- utf *classname;
-
- TYPEINFO_ASSERT(value);
-
- c = value->typeclass;
-
- /* assignments of primitive values are not checked here. */
- if (!c.any && !dest.any)
- return typecheck_TRUE;
-
- /* primitive and reference types are not assignment compatible. */
- if (!c.any || !dest.any)
- return typecheck_FALSE;
-
- /* the null type can be assigned to any type */
- if (TYPEINFO_IS_NULLTYPE(*value))
- return typecheck_TRUE;
-
- /* uninitialized objects are not assignable */
- if (TYPEINFO_IS_NEWOBJECT(*value))
- return typecheck_FALSE;
-
- if (IS_CLASSREF(c)) {
- /* The value type is an unresolved class reference. */
- classname = c.ref->name;
- }
- else {
- classname = c.cls->name;
- }
-
- if (IS_CLASSREF(dest)) {
- /* the destination type is an unresolved class reference */
- /* In this case we cannot tell a lot about assignability. */
-
- /* the common case of value and dest type having the same classname */
- if (dest.ref->name == classname && !value->merged)
- return typecheck_TRUE;
-
- /* we cannot tell if value is assignable to dest, so we */
- /* leave it up to the resolving code to check this */
- return typecheck_MAYBE;
- }
-
- /* { we know that dest is a loaded class } */
-
- if (IS_CLASSREF(c)) {
- /* the value type is an unresolved class reference */
-
- /* the common case of value and dest type having the same classname */
- if (dest.cls->name == classname)
- return typecheck_TRUE;
-
- /* we cannot tell if value is assignable to dest, so we */
- /* leave it up to the resolving code to check this */
- return typecheck_MAYBE;
- }
-
- /* { we know that both c and dest are loaded classes } */
- /* (c may still have a merged list containing unresolved classrefs!) */
-
- TYPEINFO_ASSERT(!IS_CLASSREF(c));
- TYPEINFO_ASSERT(!IS_CLASSREF(dest));
-
- cls = c.cls;
-
- TYPEINFO_ASSERT(cls->state & CLASS_LOADED);
- TYPEINFO_ASSERT(dest.cls->state & CLASS_LOADED);
-
- /* maybe we need to link the classes */
- if (!(cls->state & CLASS_LINKED))
- if (!link_class(cls))
- return typecheck_FAIL;
- if (!(dest.cls->state & CLASS_LINKED))
- if (!link_class(dest.cls))
- return typecheck_FAIL;
-
- /* { we know that both c and dest are linked classes } */
- TYPEINFO_ASSERT(cls->state & CLASS_LINKED);
- TYPEINFO_ASSERT(dest.cls->state & CLASS_LINKED);
-
- if (dest.cls->flags & ACC_INTERFACE) {
- /* We are assigning to an interface type. */
- return merged_implements_interface(cls,value->merged,dest.cls);
- }
-
- if (CLASSINFO_IS_ARRAY(dest.cls)) {
- arraydescriptor *arraydesc = dest.cls->vftbl->arraydesc;
- int dimension = arraydesc->dimension;
- classinfo *elementclass = (arraydesc->elementvftbl)
- ? arraydesc->elementvftbl->clazz : NULL;
-
- /* We are assigning to an array type. */
- if (!TYPEINFO_IS_ARRAY(*value))
- return typecheck_FALSE;
-
- /* {Both value and dest.cls are array types.} */
-
- /* value must have at least the dimension of dest.cls. */
- if (value->dimension < dimension)
- return typecheck_FALSE;
-
- if (value->dimension > dimension) {
- /* value has higher dimension so we need to check
- * if its component array can be assigned to the
- * element type of dest.cls */
-
- if (!elementclass) return typecheck_FALSE;
-
- if (elementclass->flags & ACC_INTERFACE) {
- /* We are assigning to an interface type. */
- return classinfo_implements_interface(pseudo_class_Arraystub,
- elementclass);
- }
-
- /* We are assigning to a class type. */
- return class_issubclass(pseudo_class_Arraystub,elementclass);
- }
-
- /* {value and dest.cls have the same dimension} */
-
- if (value->elementtype != arraydesc->elementtype)
- return typecheck_FALSE;
-
- if (value->elementclass.any) {
- /* We are assigning an array of objects so we have to
- * check if the elements are assignable.
- */
-
- if (elementclass->flags & ACC_INTERFACE) {
- /* We are assigning to an interface type. */
-
- return merged_implements_interface(value->elementclass.cls,
- value->merged,
- elementclass);
- }
-
- /* We are assigning to a class type. */
- return merged_is_subclass(value->elementclass.cls,value->merged,elementclass);
- }
-
- return typecheck_TRUE;
- }
-
- /* {dest.cls is not an array} */
- /* {dest.cls is a loaded class} */
-
- /* If there are any unresolved references in the merged list, we cannot */
- /* tell if the assignment will be ok. */
- /* This can only happen when cls is java.lang.Object */
- if (cls == class_java_lang_Object && value->merged) {
- classref_or_classinfo *mlist = value->merged->list;
- int i = value->merged->count;
- while (i--)
- if (IS_CLASSREF(*mlist++))
- return typecheck_MAYBE;
- }
-
- /* We are assigning to a class type */
- if (cls->flags & ACC_INTERFACE)
- cls = class_java_lang_Object;
-
- return merged_is_subclass(cls,value->merged,dest.cls);
-}
-
-/* typeinfo_is_assignable ******************************************************
-
- Check if a type is assignable to a given type.
-
- IN:
- value............the type of the value
- dest.............the type of the destination, must not be a merged type
-
- RETURN VALUE:
- typecheck_TRUE...the type is assignable
- typecheck_FALSE..the type is not assignable
- typecheck_MAYBE..check cannot be performed now because of unresolved
- classes
- typecheck_FAIL...an exception has been thrown
-
-*******************************************************************************/
-
-typecheck_result
-typeinfo_is_assignable(typeinfo_t *value,typeinfo_t *dest)
-{
- TYPEINFO_ASSERT(value);
- TYPEINFO_ASSERT(dest);
- TYPEINFO_ASSERT(dest->merged == NULL);
-
- return typeinfo_is_assignable_to_class(value,dest->typeclass);
-}
-
-/**********************************************************************/
-/* INITIALIZATION FUNCTIONS */
-/* The following functions fill in uninitialized typeinfo structures. */
-/**********************************************************************/
-
-/* typeinfo_init_classinfo *****************************************************
-
- Initialize a typeinfo to a resolved class.
-
- IN:
- c................the class
-
- OUT:
- *info............is initialized
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-void
-typeinfo_init_classinfo(typeinfo_t *info, classinfo *c)
-{
- if ((info->typeclass.cls = c)->vftbl->arraydesc) {
- if (c->vftbl->arraydesc->elementvftbl)
- info->elementclass.cls = c->vftbl->arraydesc->elementvftbl->clazz;
- else
- info->elementclass.any = NULL;
- info->dimension = c->vftbl->arraydesc->dimension;
- info->elementtype = c->vftbl->arraydesc->elementtype;
- }
- else {
- info->elementclass.any = NULL;
- info->dimension = 0;
- info->elementtype = 0;
- }
- info->merged = NULL;
-}
-
-/* typeinfo_init_class *********************************************************
-
- Initialize a typeinfo to a possibly unresolved class type.
-
- IN:
- c................the class type
-
- OUT:
- *info............is initialized
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool
-typeinfo_init_class(typeinfo_t *info,classref_or_classinfo c)
-{
- char *utf_ptr;
- int len;
- classinfo *cls;
-
- TYPEINFO_ASSERT(c.any);
- TYPEINFO_ASSERT(info);
-
- /* if necessary, try to resolve lazily */
- if (!resolve_classref_or_classinfo(NULL /* XXX should know method */,
- c,resolveLazy,false,true,&cls))
- {
- return false;
- }
-
- if (cls) {
- typeinfo_init_classinfo(info,cls);
- return true;
- }
-
- /* {the type could no be resolved lazily} */
-
- info->typeclass.ref = c.ref;
- info->elementclass.any = NULL;
- info->dimension = 0;
- info->merged = NULL;
-
- /* handle array type references */
- utf_ptr = c.ref->name->text;
- len = c.ref->name->blength;
- if (*utf_ptr == '[') {
- /* count dimensions */
- while (*utf_ptr == '[') {
- utf_ptr++;
- info->dimension++;
- len--;
- }
- if (*utf_ptr == 'L') {
- utf_ptr++;
- len -= 2;
- info->elementtype = ARRAYTYPE_OBJECT;
- info->elementclass.ref = class_get_classref(c.ref->referer,utf_new(utf_ptr,len));
- }
- else {
- /* an array with primitive element type */
- /* should have been resolved above */
- TYPEINFO_ASSERT(false);
- }
- }
- return true;
-}
-
-/* typeinfo_init_from_typedesc *************************************************
-
- Initialize a typeinfo from a typedesc.
-
- IN:
- desc.............the typedesc
-
- OUT:
- *type............set to the TYPE_* constant of DESC (if type != NULL)
- *info............receives the typeinfo (if info != NULL)
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool
-typeinfo_init_from_typedesc(typedesc *desc,u1 *type,typeinfo_t *info)
-{
- TYPEINFO_ASSERT(desc);
-
-#ifdef TYPEINFO_VERBOSE
- fprintf(stderr,"typeinfo_init_from_typedesc(");
- descriptor_debug_print_typedesc(stderr,desc);
- fprintf(stderr,")\n");
-#endif
-
- if (type)
- *type = desc->type;
-
- if (info) {
- if (desc->type == TYPE_ADR) {
- TYPEINFO_ASSERT(desc->classref);
- if (!typeinfo_init_class(info,CLASSREF_OR_CLASSINFO(desc->classref)))
- return false;
- }
- else {
- TYPEINFO_INIT_PRIMITIVE(*info);
- }
- }
- return true;
-}
-
-/* typeinfos_init_from_methoddesc **********************************************
-
- Initialize an array of typeinfos and u1 TYPE_* values from a methoddesc.
-
- IN:
- desc.............the methoddesc
- buflen...........number of parameters the buffer can hold
- twoword..........if true, use two parameter slots for two-word types
-
- OUT:
- *typebuf.........receives a TYPE_* constant for each parameter
- typebuf must be != NULL
- *infobuf.........receives a typeinfo for each parameter
- infobuf must be != NULL
- *returntype......receives a TYPE_* constant for the return type
- returntype may be NULL
- *returntypeinfo..receives a typeinfo for the return type
- returntypeinfo may be NULL
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
- NOTE:
- If (according to BUFLEN) the buffers are to small to hold the
- parameter types, an internal error is thrown. This must be
- avoided by checking the number of parameters and allocating enough
- space before calling this function.
-
-*******************************************************************************/
-
-bool
-typeinfos_init_from_methoddesc(methoddesc *desc,u1 *typebuf,typeinfo_t *infobuf,
- int buflen,bool twoword,
- u1 *returntype,typeinfo_t *returntypeinfo)
-{
- int i;
- int args = 0;
-
- TYPEINFO_ASSERT(desc);
- TYPEINFO_ASSERT(typebuf);
- TYPEINFO_ASSERT(infobuf);
-
-#ifdef TYPEINFO_VERBOSE
- fprintf(stderr,"typeinfos_init_from_methoddesc(");
- descriptor_debug_print_methoddesc(stderr,desc);
- fprintf(stderr,")\n");
-#endif
-
- /* check arguments */
- for (i=0; i<desc->paramcount; ++i) {
- if (++args > buflen) {
- exceptions_throw_internalerror("Buffer too small for method arguments.");
- return false;
- }
-
- if (!typeinfo_init_from_typedesc(desc->paramtypes + i,typebuf++,infobuf++))
- return false;
-
- if (twoword && (typebuf[-1] == TYPE_LNG || typebuf[-1] == TYPE_DBL)) {
- if (++args > buflen) {
- exceptions_throw_internalerror("Buffer too small for method arguments.");
- return false;
- }
-
- *typebuf++ = TYPE_VOID;
- TYPEINFO_INIT_PRIMITIVE(*infobuf);
- infobuf++;
- }
- }
-
- /* check returntype */
- if (returntype) {
- if (!typeinfo_init_from_typedesc(&(desc->returntype),returntype,returntypeinfo))
- return false;
- }
-
- return true;
-}
-
-/* typedescriptor_init_from_typedesc *******************************************
-
- Initialize a typedescriptor from a typedesc.
-
- IN:
- desc.............the typedesc
-
- OUT:
- *td..............receives the typedescriptor
- td must be != NULL
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool
-typedescriptor_init_from_typedesc(typedescriptor_t *td,
- typedesc *desc)
-{
- TYPEINFO_ASSERT(td);
- TYPEINFO_ASSERT(desc);
-
- td->type = desc->type;
- if (td->type == TYPE_ADR) {
- if (!typeinfo_init_class(&(td->typeinfo),CLASSREF_OR_CLASSINFO(desc->classref)))
- return false;
- }
- else {
- TYPEINFO_INIT_PRIMITIVE(td->typeinfo);
- }
- return true;
-}
-
-/* typeinfo_init_varinfo_from_typedesc *****************************************
-
- Initialize a varinfo from a typedesc.
-
- IN:
- desc.............the typedesc
-
- OUT:
- *var.............receives the type
- var must be != NULL
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool
-typeinfo_init_varinfo_from_typedesc(varinfo *var,
- typedesc *desc)
-{
- TYPEINFO_ASSERT(var);
- TYPEINFO_ASSERT(desc);
-
- var->type = desc->type;
- if (var->type == TYPE_ADR) {
- if (!typeinfo_init_class(&(var->typeinfo),CLASSREF_OR_CLASSINFO(desc->classref)))
- return false;
- }
- else {
- TYPEINFO_INIT_PRIMITIVE(var->typeinfo);
- }
- return true;
-}
-
-/* typeinfo_init_varinfos_from_methoddesc **************************************
-
- Initialize an array of varinfos from a methoddesc.
-
- IN:
- desc.............the methoddesc
- buflen...........number of parameters the buffer can hold
- startindex.......the zero-based index of the first parameter to
- write to the array. In other words the number of
- parameters to skip at the beginning of the methoddesc.
- map..............map from parameter indices to varinfo indices
- (indexed like jitdata.local_map)
-
- OUT:
- *vars............array receiving the varinfos
- td[0] receives the type of the
- (startindex+1)th parameter of the method
- *returntype......receives the typedescriptor of the return type.
- returntype may be NULL
-
- RETURN VALUE:
- true.............everything ok
- false............an exception has been thrown
-
- NOTE:
- If (according to BUFLEN) the buffer is to small to hold the
- parameter types, an internal error is thrown. This must be
- avoided by checking the number of parameters and allocating enough
- space before calling this function.
-
-*******************************************************************************/
-
-bool
-typeinfo_init_varinfos_from_methoddesc(varinfo *vars,
- methoddesc *desc,
- int buflen, int startindex,
- s4 *map,
- typedescriptor_t *returntype)
-{
- s4 i;
- s4 varindex;
- s4 type;
- s4 slot = 0;
-
- /* skip arguments */
- for (i=0; i<startindex; ++i) {
- slot++;
- if (IS_2_WORD_TYPE(desc->paramtypes[i].type))
- slot++;
- }
-
- /* check arguments */
- for (i=startindex; i<desc->paramcount; ++i) {
- type = desc->paramtypes[i].type;
- varindex = map[5*slot + type];
-
- slot++;
- if (IS_2_WORD_TYPE(type))
- slot++;
-
- if (varindex == UNUSED)
- continue;
-
- if (varindex >= buflen) {
- exceptions_throw_internalerror("Buffer too small for method arguments.");
- return false;
- }
-
- if (!typeinfo_init_varinfo_from_typedesc(vars + varindex, desc->paramtypes + i))
- return false;
- }
-
- /* check returntype */
- if (returntype) {
- if (!typedescriptor_init_from_typedesc(returntype,&(desc->returntype)))
- return false;
- }
-
- return true;
-}
-
-/* typedescriptors_init_from_methoddesc ****************************************
-
- Initialize an array of typedescriptors from a methoddesc.
-
- IN:
- desc.............the methoddesc
- buflen...........number of parameters the buffer can hold
- twoword..........if true, use two parameter slots for two-word types
- startindex.......the zero-based index of the first parameter to
- write to the array. In other words the number of
- parameters to skip at the beginning of the methoddesc.
-
- OUT:
- *td..............array receiving the typedescriptors.
- td[0] receives the typedescriptor of the
- (startindex+1)th parameter of the method
- *returntype......receives the typedescriptor of the return type.
- returntype may be NULL
-
- RETURN VALUE:
- >= 0.............number of typedescriptors filled in TD
- -1...............an exception has been thrown
-
- NOTE:
- If (according to BUFLEN) the buffer is to small to hold the
- parameter types, an internal error is thrown. This must be
- avoided by checking the number of parameters and allocating enough
- space before calling this function.
-
-*******************************************************************************/
-
-int
-typedescriptors_init_from_methoddesc(typedescriptor_t *td,
- methoddesc *desc,
- int buflen,bool twoword,int startindex,
- typedescriptor_t *returntype)
-{
- int i;
- int args = 0;
-
- /* check arguments */
- for (i=startindex; i<desc->paramcount; ++i) {
- if (++args > buflen) {
- exceptions_throw_internalerror("Buffer too small for method arguments.");
- return -1;
- }
-
- if (!typedescriptor_init_from_typedesc(td,desc->paramtypes + i))
- return -1;
- td++;
-
- if (twoword && (td[-1].type == TYPE_LNG || td[-1].type == TYPE_DBL)) {
- if (++args > buflen) {
- exceptions_throw_internalerror("Buffer too small for method arguments.");
- return -1;
- }
-
- td->type = TYPE_VOID;
- TYPEINFO_INIT_PRIMITIVE(td->typeinfo);
- td++;
- }
- }
-
- /* check returntype */
- if (returntype) {
- if (!typedescriptor_init_from_typedesc(returntype,&(desc->returntype)))
- return -1;
- }
-
- return args;
-}
-
-/* typeinfo_init_component *****************************************************
-
- Initialize a typeinfo with the component type of a given array type.
-
- IN:
- srcarray.........the typeinfo of the array type
-
- OUT:
- *dst.............receives the typeinfo of the component type
-
- RETURN VALUE:
- true.............success
- false............an exception has been thrown
-
-*******************************************************************************/
-
-bool
-typeinfo_init_component(typeinfo_t *srcarray,typeinfo_t *dst)
-{
- typeinfo_mergedlist_t *merged;
-
- TYPEINFO_ASSERT(srcarray);
- TYPEINFO_ASSERT(dst);
-
- if (TYPEINFO_IS_NULLTYPE(*srcarray)) {
- TYPEINFO_INIT_NULLTYPE(*dst);
- return true;
- }
-
- if (!TYPEINFO_IS_ARRAY(*srcarray)) {
- /* XXX should we make that a verify error? */
- exceptions_throw_internalerror("Trying to access component of non-array");
- return false;
- }
-
- /* save the mergedlist (maybe dst == srcarray) */
-
- merged = srcarray->merged;
-
- if (IS_CLASSREF(srcarray->typeclass)) {
- constant_classref *comp;
- comp = class_get_classref_component_of(srcarray->typeclass.ref);
-
- if (comp) {
- if (!typeinfo_init_class(dst,CLASSREF_OR_CLASSINFO(comp)))
- return false;
- }
- else {
- TYPEINFO_INIT_PRIMITIVE(*dst);
- }
- }
- else {
- vftbl_t *comp;
-
- if (!(srcarray->typeclass.cls->state & CLASS_LINKED)) {
- if (!link_class(srcarray->typeclass.cls)) {
- return false;
- }
- }
-
- TYPEINFO_ASSERT(srcarray->typeclass.cls->vftbl);
- TYPEINFO_ASSERT(srcarray->typeclass.cls->vftbl->arraydesc);
-
- comp = srcarray->typeclass.cls->vftbl->arraydesc->componentvftbl;
- if (comp)
- typeinfo_init_classinfo(dst,comp->clazz);
- else
- TYPEINFO_INIT_PRIMITIVE(*dst);
- }
-
- dst->merged = merged; /* XXX should we do a deep copy? */
- return true;
-}
-
-/* typeinfo_clone **************************************************************
-
- Create a deep copy of a typeinfo struct.
-
- IN:
- src..............the typeinfo to copy
-
- OUT:
- *dest............receives the copy
-
- NOTE:
- If src == dest this function is a nop.
-
-*******************************************************************************/
-
-void
-typeinfo_clone(typeinfo_t *src,typeinfo_t *dest)
-{
- int count;
- classref_or_classinfo *srclist,*destlist;
-
- if (src == dest)
- return;
-
- *dest = *src;
-
- if (src->merged) {
- count = src->merged->count;
- TYPEINFO_ALLOCMERGED(dest->merged,count);
- dest->merged->count = count;
-
- srclist = src->merged->list;
- destlist = dest->merged->list;
- while (count--)
- *destlist++ = *srclist++;
- }
-}
-
-/**********************************************************************/
-/* MISCELLANEOUS FUNCTIONS */
-/**********************************************************************/
-
-/* typeinfo_free ***************************************************************
-
- Free memory referenced by the given typeinfo. The typeinfo itself is not
- freed.
-
- IN:
- info.............the typeinfo
-
-*******************************************************************************/
-
-void
-typeinfo_free(typeinfo_t *info)
-{
- TYPEINFO_FREEMERGED_IF_ANY(info->merged);
- info->merged = NULL;
-}
-
-/**********************************************************************/
-/* MERGING FUNCTIONS */
-/* The following functions are used to merge the types represented by */
-/* two typeinfo structures into one typeinfo structure. */
-/**********************************************************************/
-
-static
-void
-typeinfo_merge_error(methodinfo *m,char *str,typeinfo_t *x,typeinfo_t *y) {
-#ifdef TYPEINFO_VERBOSE
- fprintf(stderr,"Error in typeinfo_merge: %s\n",str);
- fprintf(stderr,"Typeinfo x:\n");
- typeinfo_print(stderr,x,1);
- fprintf(stderr,"Typeinfo y:\n");
- typeinfo_print(stderr,y,1);
- log_text(str);
-#endif
-
- exceptions_throw_verifyerror(m, str);
-}
-
-/* Condition: clsx != clsy. */
-/* Returns: true if dest was changed (currently always true). */
-static
-bool
-typeinfo_merge_two(typeinfo_t *dest,classref_or_classinfo clsx,classref_or_classinfo clsy)
-{
- TYPEINFO_ASSERT(dest);
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- TYPEINFO_ALLOCMERGED(dest->merged,2);
- dest->merged->count = 2;
-
- TYPEINFO_ASSERT(clsx.any != clsy.any);
-
- if (clsx.any < clsy.any) {
- dest->merged->list[0] = clsx;
- dest->merged->list[1] = clsy;
- }
- else {
- dest->merged->list[0] = clsy;
- dest->merged->list[1] = clsx;
- }
-
- return true;
-}
-
-/* Returns: true if dest was changed. */
-static
-bool
-typeinfo_merge_add(typeinfo_t *dest,typeinfo_mergedlist_t *m,classref_or_classinfo cls)
-{
- int count;
- typeinfo_mergedlist_t *newmerged;
- classref_or_classinfo *mlist,*newlist;
-
- count = m->count;
- mlist = m->list;
-
- /* Check if cls is already in the mergedlist m. */
- while (count--) {
- if ((mlist++)->any == cls.any) { /* XXX check equal classrefs? */
- /* cls is in the list, so m is the resulting mergedlist */
- if (dest->merged == m)
- return false;
-
- /* We have to copy the mergedlist */
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- count = m->count;
- TYPEINFO_ALLOCMERGED(dest->merged,count);
- dest->merged->count = count;
- newlist = dest->merged->list;
- mlist = m->list;
- while (count--) {
- *newlist++ = *mlist++;
- }
- return true;
- }
- }
-
- /* Add cls to the mergedlist. */
- count = m->count;
- TYPEINFO_ALLOCMERGED(newmerged,count+1);
- newmerged->count = count+1;
- newlist = newmerged->list;
- mlist = m->list;
- while (count) {
- if (mlist->any > cls.any)
- break;
- *newlist++ = *mlist++;
- count--;
- }
- *newlist++ = cls;
- while (count--) {
- *newlist++ = *mlist++;
- }
-
- /* Put the new mergedlist into dest. */
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- dest->merged = newmerged;
-
- return true;
-}
-
-/* Returns: true if dest was changed. */
-static
-bool
-typeinfo_merge_mergedlists(typeinfo_t *dest,typeinfo_mergedlist_t *x,
- typeinfo_mergedlist_t *y)
-{
- int count = 0;
- int countx,county;
- typeinfo_mergedlist_t *temp,*result;
- classref_or_classinfo *clsx,*clsy,*newlist;
-
- /* count the elements that will be in the resulting list */
- /* (Both lists are sorted, equal elements are counted only once.) */
- clsx = x->list;
- clsy = y->list;
- countx = x->count;
- county = y->count;
- while (countx && county) {
- if (clsx->any == clsy->any) {
- clsx++;
- clsy++;
- countx--;
- county--;
- }
- else if (clsx->any < clsy->any) {
- clsx++;
- countx--;
- }
- else {
- clsy++;
- county--;
- }
- count++;
- }
- count += countx + county;
-
- /* {The new mergedlist will have count entries.} */
-
- if ((x->count != count) && (y->count == count)) {
- temp = x; x = y; y = temp;
- }
- /* {If one of x,y is already the result it is x.} */
- if (x->count == count) {
- /* x->merged is equal to the result */
- if (x == dest->merged)
- return false;
-
- if (!dest->merged || dest->merged->count != count) {
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- TYPEINFO_ALLOCMERGED(dest->merged,count);
- dest->merged->count = count;
- }
-
- newlist = dest->merged->list;
- clsx = x->list;
- while (count--) {
- *newlist++ = *clsx++;
- }
- return true;
- }
-
- /* {We have to merge two lists.} */
-
- /* allocate the result list */
- TYPEINFO_ALLOCMERGED(result,count);
- result->count = count;
- newlist = result->list;
-
- /* merge the sorted lists */
- clsx = x->list;
- clsy = y->list;
- countx = x->count;
- county = y->count;
- while (countx && county) {
- if (clsx->any == clsy->any) {
- *newlist++ = *clsx++;
- clsy++;
- countx--;
- county--;
- }
- else if (clsx->any < clsy->any) {
- *newlist++ = *clsx++;
- countx--;
- }
- else {
- *newlist++ = *clsy++;
- county--;
- }
- }
- while (countx--)
- *newlist++ = *clsx++;
- while (county--)
- *newlist++ = *clsy++;
-
- /* replace the list in dest with the result list */
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- dest->merged = result;
-
- return true;
-}
-
-/* typeinfo_merge_nonarrays ****************************************************
-
- Merge two non-array types.
-
- IN:
- x................the first type
- y................the second type
- mergedx..........merged list of the first type, may be NULL
- mergedy..........merged list of the descond type, may be NULL
-
- OUT:
- *dest............receives the resulting merged list
- *result..........receives the resulting type
-
- RETURN VALUE:
- typecheck_TRUE...*dest has been modified
- typecheck_FALSE..*dest has not been modified
- typecheck_FAIL...an exception has been thrown
-
- NOTE:
- RESULT is an extra parameter so it can point to dest->typeclass or to
- dest->elementclass.
-
-*******************************************************************************/
-
-static typecheck_result
-typeinfo_merge_nonarrays(typeinfo_t *dest,
- classref_or_classinfo *result,
- classref_or_classinfo x,classref_or_classinfo y,
- typeinfo_mergedlist_t *mergedx,
- typeinfo_mergedlist_t *mergedy)
-{
- classref_or_classinfo t;
- classinfo *tcls,*common;
- typeinfo_mergedlist_t *tmerged;
- bool changed;
- typecheck_result r;
- utf *xname;
- utf *yname;
-
- TYPEINFO_ASSERT(dest && result && x.any && y.any);
- TYPEINFO_ASSERT(x.cls != pseudo_class_Null);
- TYPEINFO_ASSERT(y.cls != pseudo_class_Null);
- TYPEINFO_ASSERT(x.cls != pseudo_class_New);
- TYPEINFO_ASSERT(y.cls != pseudo_class_New);
-
- /*--------------------------------------------------*/
- /* common cases */
- /*--------------------------------------------------*/
-
- /* Common case 1: x and y are the same class or class reference */
- /* (This case is very simple unless *both* x and y really represent
- * merges of subclasses of clsx==clsy.)
- */
- if ( (x.any == y.any) && (!mergedx || !mergedy) ) {
- return_simple_x:
- /* DEBUG */ /* log_text("return simple x"); */
- changed = (dest->merged != NULL);
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- dest->merged = NULL;
- *result = x;
- /* DEBUG */ /* log_text("returning"); */
- return changed;
- }
-
- xname = (IS_CLASSREF(x)) ? x.ref->name : x.cls->name;
- yname = (IS_CLASSREF(y)) ? y.ref->name : y.cls->name;
-
- /* Common case 2: xname == yname, at least one unresolved */
- if ((IS_CLASSREF(x) || IS_CLASSREF(y)) && (xname == yname))
- {
- /* use the loaded one if any */
- if (!IS_CLASSREF(y))
- x = y;
- goto return_simple_x;
- }
-
- /*--------------------------------------------------*/
- /* non-trivial cases */
- /*--------------------------------------------------*/
-
-#ifdef TYPEINFO_VERBOSE
- {
- typeinfo_t dbgx,dbgy;
- fprintf(stderr,"merge_nonarrays:\n");
- fprintf(stderr," ");if(IS_CLASSREF(x))fprintf(stderr,"<ref>");utf_fprint_printable_ascii(stderr,xname);fprintf(stderr,"\n");
- fprintf(stderr," ");if(IS_CLASSREF(y))fprintf(stderr,"<ref>");utf_fprint_printable_ascii(stderr,yname);fprintf(stderr,"\n");
- fflush(stderr);
- typeinfo_init_class(&dbgx,x);
- dbgx.merged = mergedx;
- typeinfo_init_class(&dbgy,y);
- dbgy.merged = mergedy;
- typeinfo_print(stderr,&dbgx,4);
- fprintf(stderr," with:\n");
- typeinfo_print(stderr,&dbgy,4);
- }
-#endif
-
- TYPEINFO_ASSERT(IS_CLASSREF(x) || (x.cls->state & CLASS_LOADED));
- TYPEINFO_ASSERT(IS_CLASSREF(y) || (y.cls->state & CLASS_LOADED));
-
- /* If y is unresolved or an interface, swap x and y. */
- if (IS_CLASSREF(y) || (!IS_CLASSREF(x) && y.cls->flags & ACC_INTERFACE))
- {
- t = x; x = y; y = t;
- tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
- }
-
- /* {We know: If only one of x,y is unresolved it is x,} */
- /* { If both x,y are resolved and only one of x,y is an interface it is x.} */
-
- if (IS_CLASSREF(x)) {
- /* {We know: x and y have different class names} */
-
- /* Check if we are merging an unresolved type with java.lang.Object */
- if (y.cls == class_java_lang_Object && !mergedy) {
- x = y;
- goto return_simple_x;
- }
-
- common = class_java_lang_Object;
- goto merge_with_simple_x;
- }
-
- /* {We know: both x and y are resolved} */
- /* {We know: If only one of x,y is an interface it is x.} */
-
- TYPEINFO_ASSERT(!IS_CLASSREF(x) && !IS_CLASSREF(y));
- TYPEINFO_ASSERT(x.cls->state & CLASS_LOADED);
- TYPEINFO_ASSERT(y.cls->state & CLASS_LOADED);
-
- /* Handle merging of interfaces: */
- if (x.cls->flags & ACC_INTERFACE) {
- /* {x.cls is an interface and mergedx == NULL.} */
-
- if (y.cls->flags & ACC_INTERFACE) {
- /* We are merging two interfaces. */
- /* {mergedy == NULL} */
-
- /* {We know that x.cls!=y.cls (see common case at beginning.)} */
- result->cls = class_java_lang_Object;
- return typeinfo_merge_two(dest,x,y);
- }
-
- /* {We know: x is an interface, y is a class.} */
-
- /* Check if we are merging an interface with java.lang.Object */
- if (y.cls == class_java_lang_Object && !mergedy) {
- x = y;
- goto return_simple_x;
- }
-
- /* If the type y implements x then the result of the merge
- * is x regardless of mergedy.
- */
-
- /* we may have to link the classes */
- if (!(x.cls->state & CLASS_LINKED))
- if (!link_class(x.cls))
- return typecheck_FAIL;
- if (!(y.cls->state & CLASS_LINKED))
- if (!link_class(y.cls))
- return typecheck_FAIL;
-
- TYPEINFO_ASSERT(x.cls->state & CLASS_LINKED);
- TYPEINFO_ASSERT(y.cls->state & CLASS_LINKED);
-
- if (CLASSINFO_IMPLEMENTS_INTERFACE(y.cls,x.cls->index))
- {
- /* y implements x, so the result of the merge is x. */
- goto return_simple_x;
- }
-
- r = mergedlist_implements_interface(mergedy,x.cls);
- if (r == typecheck_FAIL)
- return r;
- if (r == typecheck_TRUE)
- {
- /* y implements x, so the result of the merge is x. */
- goto return_simple_x;
- }
-
- /* {We know: x is an interface, the type y a class or a merge
- * of subclasses and is not guaranteed to implement x.} */
-
- common = class_java_lang_Object;
- goto merge_with_simple_x;
- }
-
- /* {We know: x and y are classes (not interfaces).} */
-
- /* we may have to link the classes */
- if (!(x.cls->state & CLASS_LINKED))
- if (!link_class(x.cls))
- return typecheck_FAIL;
- if (!(y.cls->state & CLASS_LINKED))
- if (!link_class(y.cls))
- return typecheck_FAIL;
-
- TYPEINFO_ASSERT(x.cls->state & CLASS_LINKED);
- TYPEINFO_ASSERT(y.cls->state & CLASS_LINKED);
-
- /* If *x is deeper in the inheritance hierarchy swap x and y. */
- if (x.cls->index > y.cls->index) {
- t = x; x = y; y = t;
- tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
- }
-
- /* {We know: y is at least as deep in the hierarchy as x.} */
-
- /* Find nearest common anchestor for the classes. */
-
- common = x.cls;
- tcls = y.cls;
-
- while (tcls->index > common->index)
- tcls = tcls->super;
-
- while (common != tcls) {
- common = common->super;
- tcls = tcls->super;
- }
-
- /* {common == nearest common anchestor of x and y.} */
-
- /* If x.cls==common and x is a whole class (not a merge of subclasses)
- * then the result of the merge is x.
- */
- if (x.cls == common && !mergedx) {
- goto return_simple_x;
- }
-
- if (mergedx) {
- result->cls = common;
- if (mergedy)
- return typeinfo_merge_mergedlists(dest,mergedx,mergedy);
- else
- return typeinfo_merge_add(dest,mergedx,y);
- }
-
-merge_with_simple_x:
- result->cls = common;
- if (mergedy)
- return typeinfo_merge_add(dest,mergedy,x);
- else
- return typeinfo_merge_two(dest,x,y);
-}
-
-/* typeinfo_merge **************************************************************
-
- Merge two types.
-
- IN:
- m................method for exception messages
- dest.............the first type
- y................the second type
-
- OUT:
- *dest............receives the result of the merge
-
- RETURN VALUE:
- typecheck_TRUE...*dest has been modified
- typecheck_FALSE..*dest has not been modified
- typecheck_FAIL...an exception has been thrown
-
- PRE-CONDITIONS:
- 1) *dest must be a valid initialized typeinfo
- 2) dest != y
-
-*******************************************************************************/
-
-typecheck_result
-typeinfo_merge(methodinfo *m,typeinfo_t *dest,typeinfo_t* y)
-{
- typeinfo_t *x;
- typeinfo_t *tmp;
- classref_or_classinfo common;
- classref_or_classinfo elementclass;
- int dimension;
- int elementtype;
- bool changed;
- typecheck_result r;
-
- /*--------------------------------------------------*/
- /* fast checks */
- /*--------------------------------------------------*/
-
- /* Merging something with itself is a nop */
- if (dest == y)
- return typecheck_FALSE;
-
- /* Merging two returnAddress types is ok. */
- /* Merging two different returnAddresses never happens, as the verifier */
- /* keeps them separate in order to check all the possible return paths */
- /* from JSR subroutines. */
- if (!dest->typeclass.any && !y->typeclass.any) {
- TYPEINFO_ASSERT(TYPEINFO_RETURNADDRESS(*dest) == TYPEINFO_RETURNADDRESS(*y));
- return typecheck_FALSE;
- }
-
- /* Primitive types cannot be merged with reference types */
- /* This must be checked before calls to typeinfo_merge. */
- TYPEINFO_ASSERT(dest->typeclass.any && y->typeclass.any);
-
- /* handle uninitialized object types */
- if (TYPEINFO_IS_NEWOBJECT(*dest) || TYPEINFO_IS_NEWOBJECT(*y)) {
- if (!TYPEINFO_IS_NEWOBJECT(*dest) || !TYPEINFO_IS_NEWOBJECT(*y)) {
- typeinfo_merge_error(m,"Trying to merge uninitialized object type.",dest,y);
- return typecheck_FAIL;
- }
- if (TYPEINFO_NEWOBJECT_INSTRUCTION(*dest) != TYPEINFO_NEWOBJECT_INSTRUCTION(*y)) {
- typeinfo_merge_error(m,"Trying to merge different uninitialized objects.",dest,y);
- return typecheck_FAIL;
- }
- /* the same uninitialized object -- no change */
- return typecheck_FALSE;
- }
-
- /*--------------------------------------------------*/
- /* common cases */
- /*--------------------------------------------------*/
-
- /* Common case: dest and y are the same class or class reference */
- /* (This case is very simple unless *both* dest and y really represent
- * merges of subclasses of class dest==class y.)
- */
- if ((dest->typeclass.any == y->typeclass.any) && (!dest->merged || !y->merged)) {
-return_simple:
- changed = (dest->merged != NULL);
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- dest->merged = NULL;
- return changed;
- }
-
- /* Handle null types: */
- if (TYPEINFO_IS_NULLTYPE(*y)) {
- return typecheck_FALSE;
- }
- if (TYPEINFO_IS_NULLTYPE(*dest)) {
- TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
- TYPEINFO_CLONE(*y,*dest);
- return typecheck_TRUE;
- }
-
- /* Common case: two types with the same name, at least one unresolved */
- if (IS_CLASSREF(dest->typeclass)) {
- if (IS_CLASSREF(y->typeclass)) {
- if (dest->typeclass.ref->name == y->typeclass.ref->name)
- goto return_simple;
- }
- else {
- /* XXX should we take y instead of dest here? */
- if (dest->typeclass.ref->name == y->typeclass.cls->name)
- goto return_simple;
- }
- }
- else {
- if (IS_CLASSREF(y->typeclass)
- && (dest->typeclass.cls->name == y->typeclass.ref->name))
- {
- goto return_simple;
- }
- }
-
- /*--------------------------------------------------*/
- /* non-trivial cases */
- /*--------------------------------------------------*/
-
-#ifdef TYPEINFO_VERBOSE
- fprintf(stderr,"merge:\n");
- typeinfo_print(stderr,dest,4);
- typeinfo_print(stderr,y,4);
-#endif
-
- /* This function uses x internally, so x and y can be swapped
- * without changing dest. */
- x = dest;
- changed = false;
-
- /* Handle merging of arrays: */
- if (TYPEINFO_IS_ARRAY(*x) && TYPEINFO_IS_ARRAY(*y)) {
-
- /* Make x the one with lesser dimension */
- if (x->dimension > y->dimension) {
- tmp = x; x = y; y = tmp;
- }
-
- /* If one array (y) has higher dimension than the other,
- * interpret it as an array (same dim. as x) of Arraystubs. */
- if (x->dimension < y->dimension) {
- dimension = x->dimension;
- elementtype = ARRAYTYPE_OBJECT;
- elementclass.cls = pseudo_class_Arraystub;
- }
- else {
- dimension = y->dimension;
- elementtype = y->elementtype;
- elementclass = y->elementclass;
- }
-
- /* {The arrays are of the same dimension.} */
-
- if (x->elementtype != elementtype) {
- /* Different element types are merged, so the resulting array
- * type has one accessible dimension less. */
- if (--dimension == 0) {
- common.cls = pseudo_class_Arraystub;
- elementtype = 0;
- elementclass.any = NULL;
- }
- else {
- common.cls = class_multiarray_of(dimension,pseudo_class_Arraystub,true);
- if (!common.cls) {
- exceptions_throw_internalerror("XXX Coult not create array class");
- return typecheck_FAIL;
- }
-
- elementtype = ARRAYTYPE_OBJECT;
- elementclass.cls = pseudo_class_Arraystub;
- }
- }
- else {
- /* {The arrays have the same dimension and elementtype.} */
-
- if (elementtype == ARRAYTYPE_OBJECT) {
- /* The elements are references, so their respective
- * types must be merged.
- */
- r = typeinfo_merge_nonarrays(dest,
- &elementclass,
- x->elementclass,
- elementclass,
- x->merged,y->merged);
- TYPEINFO_ASSERT(r != typecheck_MAYBE);
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
-
- /* DEBUG */ /* log_text("finding resulting array class: "); */
- if (IS_CLASSREF(elementclass))
- common.ref = class_get_classref_multiarray_of(dimension,elementclass.ref);
- else {
- common.cls = class_multiarray_of(dimension,elementclass.cls,true);
- if (!common.cls) {
- exceptions_throw_internalerror("XXX Coult not create array class");
- return typecheck_FAIL;
- }
- }
- /* DEBUG */ /* utf_display_printable_ascii(common->name); printf("\n"); */
- }
- else {
- common.any = y->typeclass.any;
- }
- }
- }
- else {
- /* {We know that at least one of x or y is no array, so the
- * result cannot be an array.} */
-
- r = typeinfo_merge_nonarrays(dest,
- &common,
- x->typeclass,y->typeclass,
- x->merged,y->merged);
- TYPEINFO_ASSERT(r != typecheck_MAYBE);
- if (r == typecheck_FAIL)
- return r;
- changed |= r;
-
- dimension = 0;
- elementtype = 0;
- elementclass.any = NULL;
- }
-
- /* Put the new values into dest if neccessary. */
-
- if (dest->typeclass.any != common.any) {
- dest->typeclass.any = common.any;
- changed = true;
- }
- if (dest->dimension != dimension) {
- dest->dimension = dimension;
- changed = true;
- }
- if (dest->elementtype != elementtype) {
- dest->elementtype = elementtype;
- changed = true;
- }
- if (dest->elementclass.any != elementclass.any) {
- dest->elementclass.any = elementclass.any;
- changed = true;
- }
-
- return changed;
-}
-#endif /* ENABLE_VERIFER */
-
-
-/**********************************************************************/
-/* DEBUGGING HELPERS */
-/**********************************************************************/
-
-#ifdef TYPEINFO_DEBUG
-
-#if 0
-static int
-typeinfo_test_compare(classref_or_classinfo *a,classref_or_classinfo *b)
-{
- if (a->any == b->any) return 0;
- if (a->any < b->any) return -1;
- return +1;
-}
-
-static void
-typeinfo_test_parse(typeinfo_t *info,char *str)
-{
- int num;
- int i;
- typeinfo_t *infobuf;
- u1 *typebuf;
- int returntype;
- utf *desc = utf_new_char(str);
-
- num = typeinfo_count_method_args(desc,false);
- if (num) {
- typebuf = DMNEW(u1,num);
- infobuf = DMNEW(typeinfo_t,num);
-
- typeinfo_init_from_method_args(desc,typebuf,infobuf,num,false,
- &returntype,info);
-
- TYPEINFO_ALLOCMERGED(info->merged,num);
- info->merged->count = num;
-
- for (i=0; i<num; ++i) {
- if (typebuf[i] != TYPE_ADR) {
- log_text("non-reference type in mergedlist");
- assert(0);
- }
-
- info->merged->list[i].any = infobuf[i].typeclass.any;
- }
- qsort(info->merged->list,num,sizeof(classref_or_classinfo),
- (int(*)(const void *,const void *))&typeinfo_test_compare);
- }
- else {
- typeinfo_init_from_method_args(desc,NULL,NULL,0,false,
- &returntype,info);
- }
-}
-#endif
-
-#define TYPEINFO_TEST_BUFLEN 4000
-
-static bool
-typeinfo_equal(typeinfo_t *x,typeinfo_t *y)
-{
- int i;
-
- if (x->typeclass.any != y->typeclass.any) return false;
- if (x->dimension != y->dimension) return false;
- if (x->dimension) {
- if (x->elementclass.any != y->elementclass.any) return false;
- if (x->elementtype != y->elementtype) return false;
- }
-
- if (TYPEINFO_IS_NEWOBJECT(*x))
- if (TYPEINFO_NEWOBJECT_INSTRUCTION(*x)
- != TYPEINFO_NEWOBJECT_INSTRUCTION(*y))
- return false;
-
- if (x->merged || y->merged) {
- if (!(x->merged && y->merged)) return false;
- if (x->merged->count != y->merged->count) return false;
- for (i=0; i<x->merged->count; ++i)
- if (x->merged->list[i].any != y->merged->list[i].any)
- return false;
- }
- return true;
-}
-
-static void
-typeinfo_testmerge(typeinfo_t *a,typeinfo_t *b,typeinfo_t *result,int *failed)
-{
- typeinfo_t dest;
- bool changed,changed_should_be;
- typecheck_result r;
-
- TYPEINFO_CLONE(*a,dest);
-
- printf("\n ");
- typeinfo_print_short(stdout,&dest);
- printf("\n ");
- typeinfo_print_short(stdout,b);
- printf("\n");
-
- r = typeinfo_merge(NULL,&dest,b);
- if (r == typecheck_FAIL) {
- printf("EXCEPTION\n");
- return;
- }
- changed = (r) ? 1 : 0;
- changed_should_be = (!typeinfo_equal(&dest,a)) ? 1 : 0;
-
- printf(" %s\n",(changed) ? "changed" : "=");
-
- if (typeinfo_equal(&dest,result)) {
- printf("OK ");
- typeinfo_print_short(stdout,&dest);
- printf("\n");
- if (changed != changed_should_be) {
- printf("WRONG RETURN VALUE!\n");
- (*failed)++;
- }
- }
- else {
- printf("RESULT ");
- typeinfo_print_short(stdout,&dest);
- printf("\n");
- printf("SHOULD BE ");
- typeinfo_print_short(stdout,result);
- printf("\n");
- (*failed)++;
- }
-}
-
-#if 0
-static void
-typeinfo_inc_dimension(typeinfo_t *info)
-{
- if (info->dimension++ == 0) {
- info->elementtype = ARRAYTYPE_OBJECT;
- info->elementclass = info->typeclass;
- }
- info->typeclass = class_array_of(info->typeclass,true);
-}
-#endif
-
-#define TYPEINFO_TEST_MAXDIM 10
-
-static void
-typeinfo_testrun(char *filename)
-{
- char buf[TYPEINFO_TEST_BUFLEN];
- char bufa[TYPEINFO_TEST_BUFLEN];
- char bufb[TYPEINFO_TEST_BUFLEN];
- char bufc[TYPEINFO_TEST_BUFLEN];
- typeinfo_t a,b,c;
- int maxdim;
- int failed = 0;
- FILE *file = fopen(filename,"rt");
- int res;
-
- if (!file) {
- log_text("could not open typeinfo test file");
- assert(0);
- }
-
- while (fgets(buf,TYPEINFO_TEST_BUFLEN,file)) {
- if (buf[0] == '#' || !strlen(buf))
- continue;
-
- res = sscanf(buf,"%s\t%s\t%s\n",bufa,bufb,bufc);
- if (res != 3 || !strlen(bufa) || !strlen(bufb) || !strlen(bufc)) {
- log_text("Invalid line in typeinfo test file (none of empty, comment or test)");
- assert(0);
- }
-
-#if 0
- typeinfo_test_parse(&a,bufa);
- typeinfo_test_parse(&b,bufb);
- typeinfo_test_parse(&c,bufc);
-#endif
-#if 0
- do {
-#endif
- typeinfo_testmerge(&a,&b,&c,&failed); /* check result */
- typeinfo_testmerge(&b,&a,&c,&failed); /* check commutativity */
-
- if (TYPEINFO_IS_NULLTYPE(a)) break;
- if (TYPEINFO_IS_NULLTYPE(b)) break;
- if (TYPEINFO_IS_NULLTYPE(c)) break;
-
- maxdim = a.dimension;
- if (b.dimension > maxdim) maxdim = b.dimension;
- if (c.dimension > maxdim) maxdim = c.dimension;
-
-#if 0
- if (maxdim < TYPEINFO_TEST_MAXDIM) {
- typeinfo_inc_dimension(&a);
- typeinfo_inc_dimension(&b);
- typeinfo_inc_dimension(&c);
- }
- } while (maxdim < TYPEINFO_TEST_MAXDIM);
-#endif
- }
-
- fclose(file);
-
- if (failed) {
- fprintf(stderr,"Failed typeinfo_merge tests: %d\n",failed);
- log_text("Failed test");
- assert(0);
- }
-}
-
-void
-typeinfo_test()
-{
- log_text("Running typeinfo test file...");
- typeinfo_testrun("typeinfo.tst");
- log_text("Finished typeinfo test file.");
-}
-
-#if 0
-void
-typeinfo_init_from_fielddescriptor(typeinfo_t *info,char *desc)
-{
- typeinfo_init_from_descriptor(info,desc,desc+strlen(desc));
-}
-#endif
-
-#define TYPEINFO_MAXINDENT 80
-
-void
-typeinfo_print_class(FILE *file,classref_or_classinfo c)
-{
- /*fprintf(file,"<class %p>",c.any);*/
-
- if (!c.any) {
- fprintf(file,"<null>");
- }
- else {
- if (IS_CLASSREF(c)) {
- fprintf(file,"<ref>");
- utf_fprint_printable_ascii(file,c.ref->name);
- }
- else {
- utf_fprint_printable_ascii(file,c.cls->name);
- }
- }
-}
-
-void
-typeinfo_print(FILE *file,typeinfo_t *info,int indent)
-{
- int i;
- char ind[TYPEINFO_MAXINDENT + 1];
- instruction *ins;
- basicblock *bptr;
-
- if (indent > TYPEINFO_MAXINDENT) indent = TYPEINFO_MAXINDENT;
-
- for (i=0; i<indent; ++i)
- ind[i] = ' ';
- ind[i] = (char) 0;
-
- if (TYPEINFO_IS_PRIMITIVE(*info)) {
- bptr = (basicblock*) TYPEINFO_RETURNADDRESS(*info);
- if (bptr)
- fprintf(file,"%sreturnAddress (L%03d)\n",ind,bptr->nr);
- else
- fprintf(file,"%sprimitive\n",ind);
- return;
- }
-
- if (TYPEINFO_IS_NULLTYPE(*info)) {
- fprintf(file,"%snull\n",ind);
- return;
- }
-
- if (TYPEINFO_IS_NEWOBJECT(*info)) {
- ins = (instruction *) TYPEINFO_NEWOBJECT_INSTRUCTION(*info);
- if (ins) {
- fprintf(file,"%sNEW(%p):",ind,(void*)ins);
- typeinfo_print_class(file,ins[-1].sx.val.c);
- fprintf(file,"\n");
- }
- else {
- fprintf(file,"%sNEW(this)",ind);
- }
- return;
- }
-
- fprintf(file,"%sClass: ",ind);
- typeinfo_print_class(file,info->typeclass);
- fprintf(file,"\n");
-
- if (TYPEINFO_IS_ARRAY(*info)) {
- fprintf(file,"%sDimension: %d",ind,(int)info->dimension);
- fprintf(file,"\n%sElements: ",ind);
- switch (info->elementtype) {
- case ARRAYTYPE_INT : fprintf(file,"int\n"); break;
- case ARRAYTYPE_LONG : fprintf(file,"long\n"); break;
- case ARRAYTYPE_FLOAT : fprintf(file,"float\n"); break;
- case ARRAYTYPE_DOUBLE : fprintf(file,"double\n"); break;
- case ARRAYTYPE_BYTE : fprintf(file,"byte\n"); break;
- case ARRAYTYPE_CHAR : fprintf(file,"char\n"); break;
- case ARRAYTYPE_SHORT : fprintf(file,"short\n"); break;
- case ARRAYTYPE_BOOLEAN : fprintf(file,"boolean\n"); break;
-
- case ARRAYTYPE_OBJECT:
- typeinfo_print_class(file,info->elementclass);
- fprintf(file,"\n");
- break;
-
- default:
- fprintf(file,"INVALID ARRAYTYPE!\n");
- }
- }
-
- if (info->merged) {
- fprintf(file,"%sMerged: ",ind);
- for (i=0; i<info->merged->count; ++i) {
- if (i) fprintf(file,", ");
- typeinfo_print_class(file,info->merged->list[i]);
- }
- fprintf(file,"\n");
- }
-}
-
-void
-typeinfo_print_short(FILE *file,typeinfo_t *info)
-{
- int i;
- instruction *ins;
- basicblock *bptr;
-
- /*fprintf(file,"<typeinfo %p>",info);*/
-
- if (!info) {
- fprintf(file,"(typeinfo*)NULL");
- return;
- }
-
- if (TYPEINFO_IS_PRIMITIVE(*info)) {
- bptr = (basicblock*) TYPEINFO_RETURNADDRESS(*info);
- if (bptr)
- fprintf(file,"ret(L%03d)",bptr->nr);
- else
- fprintf(file,"primitive");
- return;
- }
-
- if (TYPEINFO_IS_NULLTYPE(*info)) {
- fprintf(file,"null");
- return;
- }
-
- if (TYPEINFO_IS_NEWOBJECT(*info)) {
- ins = (instruction *) TYPEINFO_NEWOBJECT_INSTRUCTION(*info);
- if (ins) {
- /*fprintf(file,"<ins %p>",ins);*/
- fprintf(file,"NEW(%p):",(void*)ins);
- typeinfo_print_class(file,ins[-1].sx.val.c);
- }
- else
- fprintf(file,"NEW(this)");
- return;
- }
-
- typeinfo_print_class(file,info->typeclass);
-
- if (info->merged) {
- fprintf(file,"{");
- for (i=0; i<info->merged->count; ++i) {
- if (i) fprintf(file,",");
- typeinfo_print_class(file,info->merged->list[i]);
- }
- fprintf(file,"}");
- }
-}
-
-void
-typeinfo_print_type(FILE *file,int type,typeinfo_t *info)
-{
- switch (type) {
- case TYPE_VOID: fprintf(file,"V"); break;
- case TYPE_INT: fprintf(file,"I"); break;
- case TYPE_FLT: fprintf(file,"F"); break;
- case TYPE_DBL: fprintf(file,"D"); break;
- case TYPE_LNG: fprintf(file,"J"); break;
- case TYPE_RET: fprintf(file,"R:"); /* FALLTHROUGH! */
- case TYPE_ADR:
- typeinfo_print_short(file,info);
- break;
-
- default:
- fprintf(file,"!");
- }
-}
-
-void
-typedescriptor_print(FILE *file,typedescriptor_t *td)
-{
- typeinfo_print_type(file,td->type,&(td->typeinfo));
-}
-
-void
-typevector_print(FILE *file,varinfo *vec,int size)
-{
- int i;
-
- for (i=0; i<size; ++i) {
- fprintf(file," %d=",i);
- typeinfo_print_type(file, vec[i].type, &(vec[i].typeinfo));
- }
-}
-
-#endif /* TYPEINFO_DEBUG */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/jit/verify/typeinfo.c - type system used by the type checker
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "mm/memory.h"
+
+#include "toolbox/logging.h"
+
+#include "vm/array.hpp"
+#include "vm/class.hpp"
+#include "vm/descriptor.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/globals.hpp"
+#include "vm/loader.hpp"
+#include "vm/primitive.hpp"
+#include "vm/resolve.hpp"
+
+#include "vm/jit/jit.hpp"
+#include "vm/jit/verify/typeinfo.hpp"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* check if a linked class is an array class. Only use for linked classes! */
+#define CLASSINFO_IS_ARRAY(clsinfo) ((clsinfo)->vftbl->arraydesc != NULL)
+
+/* check if a linked class implements the interface with the given index */
+#define CLASSINFO_IMPLEMENTS_INTERFACE(cls,index) \
+ ( ((index) < (cls)->vftbl->interfacetablelength) \
+ && ( (cls)->vftbl->interfacetable[-(index)] != NULL ) )
+
+/******************************************************************************/
+/* DEBUG HELPERS */
+/******************************************************************************/
+
+#ifdef TYPEINFO_DEBUG
+#define TYPEINFO_ASSERT(cond) assert(cond)
+#else
+#define TYPEINFO_ASSERT(cond)
+#endif
+
+/**********************************************************************/
+/* TYPEVECTOR FUNCTIONS */
+/**********************************************************************/
+
+#if defined(ENABLE_VERIFIER)
+
+/* typevector_copy *************************************************************
+
+ Return a copy of the given typevector.
+
+ IN:
+ src..............typevector set to copy, must be != NULL
+ size.............number of elements per typevector
+
+ RETURN VALUE:
+ a pointer to the new typevector set
+
+*******************************************************************************/
+
+varinfo *
+typevector_copy(varinfo *src, int size)
+{
+ varinfo *dst;
+
+ TYPEINFO_ASSERT(src);
+
+ dst = DNEW_TYPEVECTOR(size);
+ memcpy(dst,src,TYPEVECTOR_SIZE(size));
+
+ return dst;
+}
+
+/* typevector_copy_inplace *****************************************************
+
+ Copy a typevector to a given destination.
+
+ IN:
+ src..............typevector to copy, must be != NULL
+ dst..............destination to write the copy to
+ size.............number of elements per typevector
+
+*******************************************************************************/
+
+void
+typevector_copy_inplace(varinfo *src,varinfo *dst,int size)
+{
+ memcpy(dst,src,TYPEVECTOR_SIZE(size));
+}
+
+/* typevector_checktype ********************************************************
+
+ Check if the typevector contains a given type at a given index.
+
+ IN:
+ vec..............typevector set, must be != NULL
+ index............index of component to check
+ type.............TYPE_* constant to check against
+
+ RETURN VALUE:
+ true if the typevector contains TYPE at INDEX,
+ false otherwise
+
+*******************************************************************************/
+
+bool
+typevector_checktype(varinfo *vec,int index,int type)
+{
+ TYPEINFO_ASSERT(vec);
+
+ return vec[index].type == type;
+}
+
+/* typevector_checkreference ***************************************************
+
+ Check if the typevector contains a reference at a given index.
+
+ IN:
+ vec..............typevector, must be != NULL
+ index............index of component to check
+
+ RETURN VALUE:
+ true if the typevector contains a reference at INDEX,
+ false otherwise
+
+*******************************************************************************/
+
+bool
+typevector_checkreference(varinfo *vec, int index)
+{
+ TYPEINFO_ASSERT(vec);
+ return TYPEDESC_IS_REFERENCE(vec[index]);
+}
+
+/* typevectorset_checkretaddr **************************************************
+
+ Check if the typevectors contains a returnAddress at a given index.
+
+ IN:
+ vec..............typevector, must be != NULL
+ index............index of component to check
+
+ RETURN VALUE:
+ true if the typevector contains a returnAddress at INDEX,
+ false otherwise
+
+*******************************************************************************/
+
+bool
+typevector_checkretaddr(varinfo *vec,int index)
+{
+ TYPEINFO_ASSERT(vec);
+ return TYPEDESC_IS_RETURNADDRESS(vec[index]);
+}
+
+/* typevector_store ************************************************************
+
+ Store a type at a given index in the typevector.
+
+ IN:
+ vec..............typevector set, must be != NULL
+ index............index of component to set
+ type.............TYPE_* constant of type to set
+ info.............typeinfo of type to set, may be NULL,
+ if TYPE != TYPE_ADR
+
+*******************************************************************************/
+
+void
+typevector_store(varinfo *vec,int index,int type,typeinfo_t *info)
+{
+ TYPEINFO_ASSERT(vec);
+
+ vec[index].type = type;
+ if (info)
+ TYPEINFO_COPY(*info,vec[index].typeinfo);
+}
+
+/* typevector_store_retaddr ****************************************************
+
+ Store a returnAddress type at a given index in the typevector.
+
+ IN:
+ vec..............typevector set, must be != NULL
+ index............index of component to set
+ info.............typeinfo of the returnAddress.
+
+*******************************************************************************/
+
+void
+typevector_store_retaddr(varinfo *vec,int index,typeinfo_t *info)
+{
+ TYPEINFO_ASSERT(vec);
+ TYPEINFO_ASSERT(TYPEINFO_IS_PRIMITIVE(*info));
+
+ vec[index].type = TYPE_ADR;
+ TYPEINFO_INIT_RETURNADDRESS(vec[index].typeinfo,
+ TYPEINFO_RETURNADDRESS(*info));
+}
+
+/* typevector_init_object ******************************************************
+
+ Replace all uninitialized object types in the typevector set which were
+ created by the given instruction by initialized object types.
+
+ IN:
+ set..............typevector set
+ ins..............instruction which created the uninitialized object type
+ initclass........class of the initialized object type to set
+ size.............number of elements per typevector
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+ XXX maybe we should do the lazy resolving before calling this function
+
+*******************************************************************************/
+
+bool
+typevector_init_object(varinfo *set,void *ins,
+ classref_or_classinfo initclass,
+ int size)
+{
+ int i;
+
+ for (i=0; i<size; ++i) {
+ if (set[i].type == TYPE_ADR
+ && TYPEINFO_IS_NEWOBJECT(set[i].typeinfo)
+ && TYPEINFO_NEWOBJECT_INSTRUCTION(set[i].typeinfo) == ins)
+ {
+ if (!typeinfo_init_class(&(set[i].typeinfo),initclass))
+ return false;
+ }
+ }
+ return true;
+}
+
+/* typevector_merge ************************************************************
+
+ Merge a typevector with another one.
+ The given typevectors must have the same number of components.
+
+ IN:
+ m................method for exception messages
+ dst..............the first typevector
+ y................the second typevector
+ size.............number of elements per typevector
+
+ OUT:
+ *dst.............the resulting typevector
+
+ RETURN VALUE:
+ typecheck_TRUE...dst has been modified
+ typecheck_FALSE..dst has not been modified
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+typecheck_result
+typevector_merge(methodinfo *m,varinfo *dst,varinfo *y,int size)
+{
+ bool changed = false;
+ typecheck_result r;
+
+ varinfo *a = dst;
+ varinfo *b = y;
+ while (size--) {
+ if (a->type != TYPE_VOID && a->type != b->type) {
+ a->type = TYPE_VOID;
+ changed = true;
+ }
+ else if (a->type == TYPE_ADR) {
+ if (TYPEINFO_IS_PRIMITIVE(a->typeinfo)) {
+ /* 'a' is a returnAddress */
+ if (!TYPEINFO_IS_PRIMITIVE(b->typeinfo)
+ || (TYPEINFO_RETURNADDRESS(a->typeinfo)
+ != TYPEINFO_RETURNADDRESS(b->typeinfo)))
+ {
+ a->type = TYPE_VOID;
+ changed = true;
+ }
+ }
+ else {
+ /* 'a' is a reference */
+ if (TYPEINFO_IS_PRIMITIVE(b->typeinfo)) {
+ a->type = TYPE_VOID;
+ changed = true;
+ }
+ else {
+ /* two reference types are merged. There cannot be */
+ /* a merge error. In the worst case we get j.l.O. */
+ r = typeinfo_merge(m,&(a->typeinfo),&(b->typeinfo));
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+ }
+ }
+ }
+ a++;
+ b++;
+ }
+ return (typecheck_result) changed;
+}
+
+/**********************************************************************/
+/* READ-ONLY FUNCTIONS */
+/* The following functions don't change typeinfo data. */
+/**********************************************************************/
+
+/* typeinfo_is_array ***********************************************************
+
+ Check whether a typeinfo describes an array type.
+
+ IN:
+ info.............the typeinfo, must be != NULL
+
+ RETURN VALUE:
+ true if INFO describes an array type.
+
+*******************************************************************************/
+
+bool
+typeinfo_is_array(typeinfo_t *info)
+{
+ TYPEINFO_ASSERT(info);
+ return TYPEINFO_IS_ARRAY(*info);
+}
+
+/* typeinfo_is_primitive_array *************************************************
+
+ Check whether a typeinfo describes a primitive array type.
+
+ IN:
+ info.............the typeinfo, must be != NULL
+
+ RETURN VALUE:
+ true if INFO describes an array of a primitive type.
+
+*******************************************************************************/
+
+bool
+typeinfo_is_primitive_array(typeinfo_t *info,int arraytype)
+{
+ TYPEINFO_ASSERT(info);
+ return TYPEINFO_IS_PRIMITIVE_ARRAY(*info,arraytype);
+}
+
+/* typeinfo_is_array_of_refs ***************************************************
+
+ Check whether a typeinfo describes an array of references type.
+
+ IN:
+ info.............the typeinfo, must be != NULL
+
+ RETURN VALUE:
+ true if INFO describes an array of a refrence type.
+
+*******************************************************************************/
+
+bool
+typeinfo_is_array_of_refs(typeinfo_t *info)
+{
+ TYPEINFO_ASSERT(info);
+ return TYPEINFO_IS_ARRAY_OF_REFS(*info);
+}
+
+/* interface_extends_interface *************************************************
+
+ Check if a resolved interface extends a given resolved interface.
+
+ IN:
+ cls..............the interface, must be linked
+ interf...........the interface to check against
+
+ RETURN VALUE:
+ true.............CLS extends INTERF
+ false............CLS does not extend INTERF
+
+*******************************************************************************/
+
+static bool
+interface_extends_interface(classinfo *cls,classinfo *interf)
+{
+ int i;
+
+ TYPEINFO_ASSERT(cls);
+ TYPEINFO_ASSERT(interf);
+ TYPEINFO_ASSERT((interf->flags & ACC_INTERFACE) != 0);
+ TYPEINFO_ASSERT((cls->flags & ACC_INTERFACE) != 0);
+ TYPEINFO_ASSERT(cls->state & CLASS_LINKED);
+
+ /* first check direct superinterfaces */
+ for (i=0; i<cls->interfacescount; ++i) {
+ if (cls->interfaces[i] == interf)
+ return true;
+ }
+
+ /* check indirect superinterfaces */
+ for (i=0; i<cls->interfacescount; ++i) {
+ if (interface_extends_interface(cls->interfaces[i],interf))
+ return true;
+ }
+
+ return false;
+}
+
+/* classinfo_implements_interface **********************************************
+
+ Check if a resolved class implements a given resolved interface.
+
+ IN:
+ cls..............the class
+ interf...........the interface
+
+ RETURN VALUE:
+ typecheck_TRUE...CLS implements INTERF
+ typecheck_FALSE..CLS does not implement INTERF
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+static typecheck_result
+classinfo_implements_interface(classinfo *cls,classinfo *interf)
+{
+ TYPEINFO_ASSERT(cls);
+ TYPEINFO_ASSERT(interf);
+ TYPEINFO_ASSERT((interf->flags & ACC_INTERFACE) != 0);
+
+ if (!(cls->state & CLASS_LINKED))
+ if (!link_class(cls))
+ return typecheck_FAIL;
+
+ if (cls->flags & ACC_INTERFACE) {
+ /* cls is an interface */
+ if (cls == interf)
+ return typecheck_TRUE;
+
+ /* check superinterfaces */
+ return (typecheck_result) interface_extends_interface(cls,interf);
+ }
+
+ TYPEINFO_ASSERT(cls->state & CLASS_LINKED);
+ return (typecheck_result) CLASSINFO_IMPLEMENTS_INTERFACE(cls,interf->index);
+}
+
+/* mergedlist_implements_interface *********************************************
+
+ Check if all the classes in a given merged list implement a given resolved
+ interface.
+
+ IN:
+ merged...........the list of merged class types
+ interf...........the interface to check against
+
+ RETURN VALUE:
+ typecheck_TRUE...all classes implement INTERF
+ typecheck_FALSE..there is at least one class that does not implement
+ INTERF
+ typecheck_MAYBE..check cannot be performed now because of unresolved
+ classes
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+static typecheck_result
+mergedlist_implements_interface(typeinfo_mergedlist_t *merged,
+ classinfo *interf)
+{
+ int i;
+ classref_or_classinfo *mlist;
+ typecheck_result r;
+
+ TYPEINFO_ASSERT(interf);
+ TYPEINFO_ASSERT((interf->flags & ACC_INTERFACE) != 0);
+
+ /* Check if there is an non-empty mergedlist. */
+ if (!merged)
+ return typecheck_FALSE;
+
+ /* If all classinfos in the (non-empty) merged array implement the
+ * interface return true, otherwise false.
+ */
+ mlist = merged->list;
+ i = merged->count;
+ while (i--) {
+ if (IS_CLASSREF(*mlist)) {
+ return typecheck_MAYBE;
+ }
+ r = classinfo_implements_interface((mlist++)->cls,interf);
+ if (r != typecheck_TRUE)
+ return r;
+ }
+ return typecheck_TRUE;
+}
+
+/* merged_implements_interface *************************************************
+
+ Check if a possible merged type implements a given resolved interface
+ interface.
+
+ IN:
+ typeclass........(common) class of the (merged) type
+ merged...........the list of merged class types
+ interf...........the interface to check against
+
+ RETURN VALUE:
+ typecheck_TRUE...the type implement INTERF
+ typecheck_FALSE..the type does not implement INTERF
+ typecheck_MAYBE..check cannot be performed now because of unresolved
+ classes
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+static typecheck_result
+merged_implements_interface(classinfo *typeclass,typeinfo_mergedlist_t *merged,
+ classinfo *interf)
+{
+ typecheck_result r;
+
+ /* primitive types don't support interfaces. */
+ if (!typeclass)
+ return typecheck_FALSE;
+
+ /* the null type can be cast to any interface type. */
+ if (typeclass == pseudo_class_Null)
+ return typecheck_TRUE;
+
+ /* check if typeclass implements the interface. */
+ r = classinfo_implements_interface(typeclass,interf);
+ if (r != typecheck_FALSE)
+ return r;
+
+ /* check the mergedlist */
+ if (!merged)
+ return typecheck_FALSE;
+ return mergedlist_implements_interface(merged,interf);
+}
+
+/* merged_is_subclass **********************************************************
+
+ Check if a possible merged type is a subclass of a given class.
+ A merged type is a subclass of a class C if all types in the merged list
+ are subclasses of C. A sufficient condition for this is that the
+ common type of the merged type is a subclass of C.
+
+ IN:
+ typeclass........(common) class of the (merged) type
+ MUST be a loaded and linked class
+ merged...........the list of merged class types
+ cls..............the class to theck against
+
+ RETURN VALUE:
+ typecheck_TRUE...the type is a subclass of CLS
+ typecheck_FALSE..the type is not a subclass of CLS
+ typecheck_MAYBE..check cannot be performed now because of unresolved
+ classes
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+static typecheck_result
+merged_is_subclass(classinfo *typeclass,typeinfo_mergedlist_t *merged,
+ classinfo *cls)
+{
+ int i;
+ classref_or_classinfo *mlist;
+
+ TYPEINFO_ASSERT(cls);
+
+ /* primitive types aren't subclasses of anything. */
+ if (!typeclass)
+ return typecheck_FALSE;
+
+ /* the null type can be cast to any reference type. */
+ if (typeclass == pseudo_class_Null)
+ return typecheck_TRUE;
+
+ TYPEINFO_ASSERT(typeclass->state & CLASS_LOADED);
+ TYPEINFO_ASSERT(typeclass->state & CLASS_LINKED);
+
+ /* check if the common typeclass is a subclass of CLS. */
+ if (class_issubclass(typeclass,cls))
+ return typecheck_TRUE;
+
+ /* check the mergedlist */
+ if (!merged)
+ return typecheck_FALSE;
+ /* If all classinfos in the (non-empty) merged list are subclasses
+ * of CLS, return true, otherwise false.
+ * If there is at least one unresolved type in the list,
+ * return typecheck_MAYBE.
+ */
+ mlist = merged->list;
+ i = merged->count;
+ while (i--) {
+ if (IS_CLASSREF(*mlist)) {
+ return typecheck_MAYBE;
+ }
+ if (!(mlist->cls->state & CLASS_LINKED))
+ if (!link_class(mlist->cls))
+ return typecheck_FAIL;
+ if (!class_issubclass(mlist->cls,cls))
+ return typecheck_FALSE;
+ mlist++;
+ }
+ return typecheck_TRUE;
+}
+
+/* typeinfo_is_assignable_to_class *********************************************
+
+ Check if a type is assignable to a given class type.
+
+ IN:
+ value............the type of the value
+ dest.............the type of the destination
+
+ RETURN VALUE:
+ typecheck_TRUE...the type is assignable
+ typecheck_FALSE..the type is not assignable
+ typecheck_MAYBE..check cannot be performed now because of unresolved
+ classes
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+typecheck_result
+typeinfo_is_assignable_to_class(typeinfo_t *value,classref_or_classinfo dest)
+{
+ classref_or_classinfo c;
+ classinfo *cls;
+ utf *classname;
+
+ TYPEINFO_ASSERT(value);
+
+ c = value->typeclass;
+
+ /* assignments of primitive values are not checked here. */
+ if (!c.any && !dest.any)
+ return typecheck_TRUE;
+
+ /* primitive and reference types are not assignment compatible. */
+ if (!c.any || !dest.any)
+ return typecheck_FALSE;
+
+ /* the null type can be assigned to any type */
+ if (TYPEINFO_IS_NULLTYPE(*value))
+ return typecheck_TRUE;
+
+ /* uninitialized objects are not assignable */
+ if (TYPEINFO_IS_NEWOBJECT(*value))
+ return typecheck_FALSE;
+
+ if (IS_CLASSREF(c)) {
+ /* The value type is an unresolved class reference. */
+ classname = c.ref->name;
+ }
+ else {
+ classname = c.cls->name;
+ }
+
+ if (IS_CLASSREF(dest)) {
+ /* the destination type is an unresolved class reference */
+ /* In this case we cannot tell a lot about assignability. */
+
+ /* the common case of value and dest type having the same classname */
+ if (dest.ref->name == classname && !value->merged)
+ return typecheck_TRUE;
+
+ /* we cannot tell if value is assignable to dest, so we */
+ /* leave it up to the resolving code to check this */
+ return typecheck_MAYBE;
+ }
+
+ /* { we know that dest is a loaded class } */
+
+ if (IS_CLASSREF(c)) {
+ /* the value type is an unresolved class reference */
+
+ /* the common case of value and dest type having the same classname */
+ if (dest.cls->name == classname)
+ return typecheck_TRUE;
+
+ /* we cannot tell if value is assignable to dest, so we */
+ /* leave it up to the resolving code to check this */
+ return typecheck_MAYBE;
+ }
+
+ /* { we know that both c and dest are loaded classes } */
+ /* (c may still have a merged list containing unresolved classrefs!) */
+
+ TYPEINFO_ASSERT(!IS_CLASSREF(c));
+ TYPEINFO_ASSERT(!IS_CLASSREF(dest));
+
+ cls = c.cls;
+
+ TYPEINFO_ASSERT(cls->state & CLASS_LOADED);
+ TYPEINFO_ASSERT(dest.cls->state & CLASS_LOADED);
+
+ /* maybe we need to link the classes */
+ if (!(cls->state & CLASS_LINKED))
+ if (!link_class(cls))
+ return typecheck_FAIL;
+ if (!(dest.cls->state & CLASS_LINKED))
+ if (!link_class(dest.cls))
+ return typecheck_FAIL;
+
+ /* { we know that both c and dest are linked classes } */
+ TYPEINFO_ASSERT(cls->state & CLASS_LINKED);
+ TYPEINFO_ASSERT(dest.cls->state & CLASS_LINKED);
+
+ if (dest.cls->flags & ACC_INTERFACE) {
+ /* We are assigning to an interface type. */
+ return merged_implements_interface(cls,value->merged,dest.cls);
+ }
+
+ if (CLASSINFO_IS_ARRAY(dest.cls)) {
+ arraydescriptor *arraydesc = dest.cls->vftbl->arraydesc;
+ int dimension = arraydesc->dimension;
+ classinfo *elementclass = (arraydesc->elementvftbl)
+ ? arraydesc->elementvftbl->clazz : NULL;
+
+ /* We are assigning to an array type. */
+ if (!TYPEINFO_IS_ARRAY(*value))
+ return typecheck_FALSE;
+
+ /* {Both value and dest.cls are array types.} */
+
+ /* value must have at least the dimension of dest.cls. */
+ if (value->dimension < dimension)
+ return typecheck_FALSE;
+
+ if (value->dimension > dimension) {
+ /* value has higher dimension so we need to check
+ * if its component array can be assigned to the
+ * element type of dest.cls */
+
+ if (!elementclass) return typecheck_FALSE;
+
+ if (elementclass->flags & ACC_INTERFACE) {
+ /* We are assigning to an interface type. */
+ return classinfo_implements_interface(pseudo_class_Arraystub,
+ elementclass);
+ }
+
+ /* We are assigning to a class type. */
+ return (typecheck_result) class_issubclass(pseudo_class_Arraystub,elementclass);
+ }
+
+ /* {value and dest.cls have the same dimension} */
+
+ if (value->elementtype != arraydesc->elementtype)
+ return typecheck_FALSE;
+
+ if (value->elementclass.any) {
+ /* We are assigning an array of objects so we have to
+ * check if the elements are assignable.
+ */
+
+ if (elementclass->flags & ACC_INTERFACE) {
+ /* We are assigning to an interface type. */
+
+ return merged_implements_interface(value->elementclass.cls,
+ value->merged,
+ elementclass);
+ }
+
+ /* We are assigning to a class type. */
+ return merged_is_subclass(value->elementclass.cls,value->merged,elementclass);
+ }
+
+ return typecheck_TRUE;
+ }
+
+ /* {dest.cls is not an array} */
+ /* {dest.cls is a loaded class} */
+
+ /* If there are any unresolved references in the merged list, we cannot */
+ /* tell if the assignment will be ok. */
+ /* This can only happen when cls is java.lang.Object */
+ if (cls == class_java_lang_Object && value->merged) {
+ classref_or_classinfo *mlist = value->merged->list;
+ int i = value->merged->count;
+ while (i--)
+ if (IS_CLASSREF(*mlist++))
+ return typecheck_MAYBE;
+ }
+
+ /* We are assigning to a class type */
+ if (cls->flags & ACC_INTERFACE)
+ cls = class_java_lang_Object;
+
+ return merged_is_subclass(cls,value->merged,dest.cls);
+}
+
+/* typeinfo_is_assignable ******************************************************
+
+ Check if a type is assignable to a given type.
+
+ IN:
+ value............the type of the value
+ dest.............the type of the destination, must not be a merged type
+
+ RETURN VALUE:
+ typecheck_TRUE...the type is assignable
+ typecheck_FALSE..the type is not assignable
+ typecheck_MAYBE..check cannot be performed now because of unresolved
+ classes
+ typecheck_FAIL...an exception has been thrown
+
+*******************************************************************************/
+
+typecheck_result
+typeinfo_is_assignable(typeinfo_t *value,typeinfo_t *dest)
+{
+ TYPEINFO_ASSERT(value);
+ TYPEINFO_ASSERT(dest);
+ TYPEINFO_ASSERT(dest->merged == NULL);
+
+ return typeinfo_is_assignable_to_class(value,dest->typeclass);
+}
+
+/**********************************************************************/
+/* INITIALIZATION FUNCTIONS */
+/* The following functions fill in uninitialized typeinfo structures. */
+/**********************************************************************/
+
+/* typeinfo_init_classinfo *****************************************************
+
+ Initialize a typeinfo to a resolved class.
+
+ IN:
+ c................the class
+
+ OUT:
+ *info............is initialized
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+void
+typeinfo_init_classinfo(typeinfo_t *info, classinfo *c)
+{
+ if ((info->typeclass.cls = c)->vftbl->arraydesc) {
+ if (c->vftbl->arraydesc->elementvftbl)
+ info->elementclass.cls = c->vftbl->arraydesc->elementvftbl->clazz;
+ else
+ info->elementclass.any = NULL;
+ info->dimension = c->vftbl->arraydesc->dimension;
+ info->elementtype = c->vftbl->arraydesc->elementtype;
+ }
+ else {
+ info->elementclass.any = NULL;
+ info->dimension = 0;
+ info->elementtype = 0;
+ }
+ info->merged = NULL;
+}
+
+/* typeinfo_init_class *********************************************************
+
+ Initialize a typeinfo to a possibly unresolved class type.
+
+ IN:
+ c................the class type
+
+ OUT:
+ *info............is initialized
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool
+typeinfo_init_class(typeinfo_t *info,classref_or_classinfo c)
+{
+ char *utf_ptr;
+ int len;
+ classinfo *cls;
+
+ TYPEINFO_ASSERT(c.any);
+ TYPEINFO_ASSERT(info);
+
+ /* if necessary, try to resolve lazily */
+ if (!resolve_classref_or_classinfo(NULL /* XXX should know method */,
+ c,resolveLazy,false,true,&cls))
+ {
+ return false;
+ }
+
+ if (cls) {
+ typeinfo_init_classinfo(info,cls);
+ return true;
+ }
+
+ /* {the type could no be resolved lazily} */
+
+ info->typeclass.ref = c.ref;
+ info->elementclass.any = NULL;
+ info->dimension = 0;
+ info->merged = NULL;
+
+ /* handle array type references */
+ utf_ptr = c.ref->name->text;
+ len = c.ref->name->blength;
+ if (*utf_ptr == '[') {
+ /* count dimensions */
+ while (*utf_ptr == '[') {
+ utf_ptr++;
+ info->dimension++;
+ len--;
+ }
+ if (*utf_ptr == 'L') {
+ utf_ptr++;
+ len -= 2;
+ info->elementtype = ARRAYTYPE_OBJECT;
+ info->elementclass.ref = class_get_classref(c.ref->referer,utf_new(utf_ptr,len));
+ }
+ else {
+ /* an array with primitive element type */
+ /* should have been resolved above */
+ TYPEINFO_ASSERT(false);
+ }
+ }
+ return true;
+}
+
+/* typeinfo_init_from_typedesc *************************************************
+
+ Initialize a typeinfo from a typedesc.
+
+ IN:
+ desc.............the typedesc
+
+ OUT:
+ *type............set to the TYPE_* constant of DESC (if type != NULL)
+ *info............receives the typeinfo (if info != NULL)
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool
+typeinfo_init_from_typedesc(typedesc *desc,u1 *type,typeinfo_t *info)
+{
+ TYPEINFO_ASSERT(desc);
+
+#ifdef TYPEINFO_VERBOSE
+ fprintf(stderr,"typeinfo_init_from_typedesc(");
+ descriptor_debug_print_typedesc(stderr,desc);
+ fprintf(stderr,")\n");
+#endif
+
+ if (type)
+ *type = desc->type;
+
+ if (info) {
+ if (desc->type == TYPE_ADR) {
+ TYPEINFO_ASSERT(desc->classref);
+ if (!typeinfo_init_class(info,CLASSREF_OR_CLASSINFO(desc->classref)))
+ return false;
+ }
+ else {
+ TYPEINFO_INIT_PRIMITIVE(*info);
+ }
+ }
+ return true;
+}
+
+/* typeinfos_init_from_methoddesc **********************************************
+
+ Initialize an array of typeinfos and u1 TYPE_* values from a methoddesc.
+
+ IN:
+ desc.............the methoddesc
+ buflen...........number of parameters the buffer can hold
+ twoword..........if true, use two parameter slots for two-word types
+
+ OUT:
+ *typebuf.........receives a TYPE_* constant for each parameter
+ typebuf must be != NULL
+ *infobuf.........receives a typeinfo for each parameter
+ infobuf must be != NULL
+ *returntype......receives a TYPE_* constant for the return type
+ returntype may be NULL
+ *returntypeinfo..receives a typeinfo for the return type
+ returntypeinfo may be NULL
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+ NOTE:
+ If (according to BUFLEN) the buffers are to small to hold the
+ parameter types, an internal error is thrown. This must be
+ avoided by checking the number of parameters and allocating enough
+ space before calling this function.
+
+*******************************************************************************/
+
+bool
+typeinfos_init_from_methoddesc(methoddesc *desc,u1 *typebuf,typeinfo_t *infobuf,
+ int buflen,bool twoword,
+ u1 *returntype,typeinfo_t *returntypeinfo)
+{
+ int i;
+ int args = 0;
+
+ TYPEINFO_ASSERT(desc);
+ TYPEINFO_ASSERT(typebuf);
+ TYPEINFO_ASSERT(infobuf);
+
+#ifdef TYPEINFO_VERBOSE
+ fprintf(stderr,"typeinfos_init_from_methoddesc(");
+ descriptor_debug_print_methoddesc(stderr,desc);
+ fprintf(stderr,")\n");
+#endif
+
+ /* check arguments */
+ for (i=0; i<desc->paramcount; ++i) {
+ if (++args > buflen) {
+ exceptions_throw_internalerror("Buffer too small for method arguments.");
+ return false;
+ }
+
+ if (!typeinfo_init_from_typedesc(desc->paramtypes + i,typebuf++,infobuf++))
+ return false;
+
+ if (twoword && (typebuf[-1] == TYPE_LNG || typebuf[-1] == TYPE_DBL)) {
+ if (++args > buflen) {
+ exceptions_throw_internalerror("Buffer too small for method arguments.");
+ return false;
+ }
+
+ *typebuf++ = TYPE_VOID;
+ TYPEINFO_INIT_PRIMITIVE(*infobuf);
+ infobuf++;
+ }
+ }
+
+ /* check returntype */
+ if (returntype) {
+ if (!typeinfo_init_from_typedesc(&(desc->returntype),returntype,returntypeinfo))
+ return false;
+ }
+
+ return true;
+}
+
+/* typedescriptor_init_from_typedesc *******************************************
+
+ Initialize a typedescriptor from a typedesc.
+
+ IN:
+ desc.............the typedesc
+
+ OUT:
+ *td..............receives the typedescriptor
+ td must be != NULL
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool
+typedescriptor_init_from_typedesc(typedescriptor_t *td,
+ typedesc *desc)
+{
+ TYPEINFO_ASSERT(td);
+ TYPEINFO_ASSERT(desc);
+
+ td->type = desc->type;
+ if (td->type == TYPE_ADR) {
+ if (!typeinfo_init_class(&(td->typeinfo),CLASSREF_OR_CLASSINFO(desc->classref)))
+ return false;
+ }
+ else {
+ TYPEINFO_INIT_PRIMITIVE(td->typeinfo);
+ }
+ return true;
+}
+
+/* typeinfo_init_varinfo_from_typedesc *****************************************
+
+ Initialize a varinfo from a typedesc.
+
+ IN:
+ desc.............the typedesc
+
+ OUT:
+ *var.............receives the type
+ var must be != NULL
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool
+typeinfo_init_varinfo_from_typedesc(varinfo *var,
+ typedesc *desc)
+{
+ TYPEINFO_ASSERT(var);
+ TYPEINFO_ASSERT(desc);
+
+ var->type = desc->type;
+ if (var->type == TYPE_ADR) {
+ if (!typeinfo_init_class(&(var->typeinfo),CLASSREF_OR_CLASSINFO(desc->classref)))
+ return false;
+ }
+ else {
+ TYPEINFO_INIT_PRIMITIVE(var->typeinfo);
+ }
+ return true;
+}
+
+/* typeinfo_init_varinfos_from_methoddesc **************************************
+
+ Initialize an array of varinfos from a methoddesc.
+
+ IN:
+ desc.............the methoddesc
+ buflen...........number of parameters the buffer can hold
+ startindex.......the zero-based index of the first parameter to
+ write to the array. In other words the number of
+ parameters to skip at the beginning of the methoddesc.
+ map..............map from parameter indices to varinfo indices
+ (indexed like jitdata.local_map)
+
+ OUT:
+ *vars............array receiving the varinfos
+ td[0] receives the type of the
+ (startindex+1)th parameter of the method
+ *returntype......receives the typedescriptor of the return type.
+ returntype may be NULL
+
+ RETURN VALUE:
+ true.............everything ok
+ false............an exception has been thrown
+
+ NOTE:
+ If (according to BUFLEN) the buffer is to small to hold the
+ parameter types, an internal error is thrown. This must be
+ avoided by checking the number of parameters and allocating enough
+ space before calling this function.
+
+*******************************************************************************/
+
+bool
+typeinfo_init_varinfos_from_methoddesc(varinfo *vars,
+ methoddesc *desc,
+ int buflen, int startindex,
+ s4 *map,
+ typedescriptor_t *returntype)
+{
+ s4 i;
+ s4 varindex;
+ s4 type;
+ s4 slot = 0;
+
+ /* skip arguments */
+ for (i=0; i<startindex; ++i) {
+ slot++;
+ if (IS_2_WORD_TYPE(desc->paramtypes[i].type))
+ slot++;
+ }
+
+ /* check arguments */
+ for (i=startindex; i<desc->paramcount; ++i) {
+ type = desc->paramtypes[i].type;
+ varindex = map[5*slot + type];
+
+ slot++;
+ if (IS_2_WORD_TYPE(type))
+ slot++;
+
+ if (varindex == UNUSED)
+ continue;
+
+ if (varindex >= buflen) {
+ exceptions_throw_internalerror("Buffer too small for method arguments.");
+ return false;
+ }
+
+ if (!typeinfo_init_varinfo_from_typedesc(vars + varindex, desc->paramtypes + i))
+ return false;
+ }
+
+ /* check returntype */
+ if (returntype) {
+ if (!typedescriptor_init_from_typedesc(returntype,&(desc->returntype)))
+ return false;
+ }
+
+ return true;
+}
+
+/* typedescriptors_init_from_methoddesc ****************************************
+
+ Initialize an array of typedescriptors from a methoddesc.
+
+ IN:
+ desc.............the methoddesc
+ buflen...........number of parameters the buffer can hold
+ twoword..........if true, use two parameter slots for two-word types
+ startindex.......the zero-based index of the first parameter to
+ write to the array. In other words the number of
+ parameters to skip at the beginning of the methoddesc.
+
+ OUT:
+ *td..............array receiving the typedescriptors.
+ td[0] receives the typedescriptor of the
+ (startindex+1)th parameter of the method
+ *returntype......receives the typedescriptor of the return type.
+ returntype may be NULL
+
+ RETURN VALUE:
+ >= 0.............number of typedescriptors filled in TD
+ -1...............an exception has been thrown
+
+ NOTE:
+ If (according to BUFLEN) the buffer is to small to hold the
+ parameter types, an internal error is thrown. This must be
+ avoided by checking the number of parameters and allocating enough
+ space before calling this function.
+
+*******************************************************************************/
+
+int
+typedescriptors_init_from_methoddesc(typedescriptor_t *td,
+ methoddesc *desc,
+ int buflen,bool twoword,int startindex,
+ typedescriptor_t *returntype)
+{
+ int i;
+ int args = 0;
+
+ /* check arguments */
+ for (i=startindex; i<desc->paramcount; ++i) {
+ if (++args > buflen) {
+ exceptions_throw_internalerror("Buffer too small for method arguments.");
+ return -1;
+ }
+
+ if (!typedescriptor_init_from_typedesc(td,desc->paramtypes + i))
+ return -1;
+ td++;
+
+ if (twoword && (td[-1].type == TYPE_LNG || td[-1].type == TYPE_DBL)) {
+ if (++args > buflen) {
+ exceptions_throw_internalerror("Buffer too small for method arguments.");
+ return -1;
+ }
+
+ td->type = TYPE_VOID;
+ TYPEINFO_INIT_PRIMITIVE(td->typeinfo);
+ td++;
+ }
+ }
+
+ /* check returntype */
+ if (returntype) {
+ if (!typedescriptor_init_from_typedesc(returntype,&(desc->returntype)))
+ return -1;
+ }
+
+ return args;
+}
+
+/* typeinfo_init_component *****************************************************
+
+ Initialize a typeinfo with the component type of a given array type.
+
+ IN:
+ srcarray.........the typeinfo of the array type
+
+ OUT:
+ *dst.............receives the typeinfo of the component type
+
+ RETURN VALUE:
+ true.............success
+ false............an exception has been thrown
+
+*******************************************************************************/
+
+bool
+typeinfo_init_component(typeinfo_t *srcarray,typeinfo_t *dst)
+{
+ typeinfo_mergedlist_t *merged;
+
+ TYPEINFO_ASSERT(srcarray);
+ TYPEINFO_ASSERT(dst);
+
+ if (TYPEINFO_IS_NULLTYPE(*srcarray)) {
+ TYPEINFO_INIT_NULLTYPE(*dst);
+ return true;
+ }
+
+ if (!TYPEINFO_IS_ARRAY(*srcarray)) {
+ /* XXX should we make that a verify error? */
+ exceptions_throw_internalerror("Trying to access component of non-array");
+ return false;
+ }
+
+ /* save the mergedlist (maybe dst == srcarray) */
+
+ merged = srcarray->merged;
+
+ if (IS_CLASSREF(srcarray->typeclass)) {
+ constant_classref *comp;
+ comp = class_get_classref_component_of(srcarray->typeclass.ref);
+
+ if (comp) {
+ if (!typeinfo_init_class(dst,CLASSREF_OR_CLASSINFO(comp)))
+ return false;
+ }
+ else {
+ TYPEINFO_INIT_PRIMITIVE(*dst);
+ }
+ }
+ else {
+ vftbl_t *comp;
+
+ if (!(srcarray->typeclass.cls->state & CLASS_LINKED)) {
+ if (!link_class(srcarray->typeclass.cls)) {
+ return false;
+ }
+ }
+
+ TYPEINFO_ASSERT(srcarray->typeclass.cls->vftbl);
+ TYPEINFO_ASSERT(srcarray->typeclass.cls->vftbl->arraydesc);
+
+ comp = srcarray->typeclass.cls->vftbl->arraydesc->componentvftbl;
+ if (comp)
+ typeinfo_init_classinfo(dst,comp->clazz);
+ else
+ TYPEINFO_INIT_PRIMITIVE(*dst);
+ }
+
+ dst->merged = merged; /* XXX should we do a deep copy? */
+ return true;
+}
+
+/* typeinfo_clone **************************************************************
+
+ Create a deep copy of a typeinfo struct.
+
+ IN:
+ src..............the typeinfo to copy
+
+ OUT:
+ *dest............receives the copy
+
+ NOTE:
+ If src == dest this function is a nop.
+
+*******************************************************************************/
+
+void
+typeinfo_clone(typeinfo_t *src,typeinfo_t *dest)
+{
+ int count;
+ classref_or_classinfo *srclist,*destlist;
+
+ if (src == dest)
+ return;
+
+ *dest = *src;
+
+ if (src->merged) {
+ count = src->merged->count;
+ TYPEINFO_ALLOCMERGED(dest->merged,count);
+ dest->merged->count = count;
+
+ srclist = src->merged->list;
+ destlist = dest->merged->list;
+ while (count--)
+ *destlist++ = *srclist++;
+ }
+}
+
+/**********************************************************************/
+/* MISCELLANEOUS FUNCTIONS */
+/**********************************************************************/
+
+/* typeinfo_free ***************************************************************
+
+ Free memory referenced by the given typeinfo. The typeinfo itself is not
+ freed.
+
+ IN:
+ info.............the typeinfo
+
+*******************************************************************************/
+
+void
+typeinfo_free(typeinfo_t *info)
+{
+ TYPEINFO_FREEMERGED_IF_ANY(info->merged);
+ info->merged = NULL;
+}
+
+/**********************************************************************/
+/* MERGING FUNCTIONS */
+/* The following functions are used to merge the types represented by */
+/* two typeinfo structures into one typeinfo structure. */
+/**********************************************************************/
+
+static
+void
+typeinfo_merge_error(methodinfo *m,char *str,typeinfo_t *x,typeinfo_t *y) {
+#ifdef TYPEINFO_VERBOSE
+ fprintf(stderr,"Error in typeinfo_merge: %s\n",str);
+ fprintf(stderr,"Typeinfo x:\n");
+ typeinfo_print(stderr,x,1);
+ fprintf(stderr,"Typeinfo y:\n");
+ typeinfo_print(stderr,y,1);
+ log_text(str);
+#endif
+
+ exceptions_throw_verifyerror(m, str);
+}
+
+/* Condition: clsx != clsy. */
+/* Returns: true if dest was changed (currently always true). */
+static
+bool
+typeinfo_merge_two(typeinfo_t *dest,classref_or_classinfo clsx,classref_or_classinfo clsy)
+{
+ TYPEINFO_ASSERT(dest);
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ TYPEINFO_ALLOCMERGED(dest->merged,2);
+ dest->merged->count = 2;
+
+ TYPEINFO_ASSERT(clsx.any != clsy.any);
+
+ if (clsx.any < clsy.any) {
+ dest->merged->list[0] = clsx;
+ dest->merged->list[1] = clsy;
+ }
+ else {
+ dest->merged->list[0] = clsy;
+ dest->merged->list[1] = clsx;
+ }
+
+ return true;
+}
+
+/* Returns: true if dest was changed. */
+static
+bool
+typeinfo_merge_add(typeinfo_t *dest,typeinfo_mergedlist_t *m,classref_or_classinfo cls)
+{
+ int count;
+ typeinfo_mergedlist_t *newmerged;
+ classref_or_classinfo *mlist,*newlist;
+
+ count = m->count;
+ mlist = m->list;
+
+ /* Check if cls is already in the mergedlist m. */
+ while (count--) {
+ if ((mlist++)->any == cls.any) { /* XXX check equal classrefs? */
+ /* cls is in the list, so m is the resulting mergedlist */
+ if (dest->merged == m)
+ return false;
+
+ /* We have to copy the mergedlist */
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ count = m->count;
+ TYPEINFO_ALLOCMERGED(dest->merged,count);
+ dest->merged->count = count;
+ newlist = dest->merged->list;
+ mlist = m->list;
+ while (count--) {
+ *newlist++ = *mlist++;
+ }
+ return true;
+ }
+ }
+
+ /* Add cls to the mergedlist. */
+ count = m->count;
+ TYPEINFO_ALLOCMERGED(newmerged,count+1);
+ newmerged->count = count+1;
+ newlist = newmerged->list;
+ mlist = m->list;
+ while (count) {
+ if (mlist->any > cls.any)
+ break;
+ *newlist++ = *mlist++;
+ count--;
+ }
+ *newlist++ = cls;
+ while (count--) {
+ *newlist++ = *mlist++;
+ }
+
+ /* Put the new mergedlist into dest. */
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ dest->merged = newmerged;
+
+ return true;
+}
+
+/* Returns: true if dest was changed. */
+static
+bool
+typeinfo_merge_mergedlists(typeinfo_t *dest,typeinfo_mergedlist_t *x,
+ typeinfo_mergedlist_t *y)
+{
+ int count = 0;
+ int countx,county;
+ typeinfo_mergedlist_t *temp,*result;
+ classref_or_classinfo *clsx,*clsy,*newlist;
+
+ /* count the elements that will be in the resulting list */
+ /* (Both lists are sorted, equal elements are counted only once.) */
+ clsx = x->list;
+ clsy = y->list;
+ countx = x->count;
+ county = y->count;
+ while (countx && county) {
+ if (clsx->any == clsy->any) {
+ clsx++;
+ clsy++;
+ countx--;
+ county--;
+ }
+ else if (clsx->any < clsy->any) {
+ clsx++;
+ countx--;
+ }
+ else {
+ clsy++;
+ county--;
+ }
+ count++;
+ }
+ count += countx + county;
+
+ /* {The new mergedlist will have count entries.} */
+
+ if ((x->count != count) && (y->count == count)) {
+ temp = x; x = y; y = temp;
+ }
+ /* {If one of x,y is already the result it is x.} */
+ if (x->count == count) {
+ /* x->merged is equal to the result */
+ if (x == dest->merged)
+ return false;
+
+ if (!dest->merged || dest->merged->count != count) {
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ TYPEINFO_ALLOCMERGED(dest->merged,count);
+ dest->merged->count = count;
+ }
+
+ newlist = dest->merged->list;
+ clsx = x->list;
+ while (count--) {
+ *newlist++ = *clsx++;
+ }
+ return true;
+ }
+
+ /* {We have to merge two lists.} */
+
+ /* allocate the result list */
+ TYPEINFO_ALLOCMERGED(result,count);
+ result->count = count;
+ newlist = result->list;
+
+ /* merge the sorted lists */
+ clsx = x->list;
+ clsy = y->list;
+ countx = x->count;
+ county = y->count;
+ while (countx && county) {
+ if (clsx->any == clsy->any) {
+ *newlist++ = *clsx++;
+ clsy++;
+ countx--;
+ county--;
+ }
+ else if (clsx->any < clsy->any) {
+ *newlist++ = *clsx++;
+ countx--;
+ }
+ else {
+ *newlist++ = *clsy++;
+ county--;
+ }
+ }
+ while (countx--)
+ *newlist++ = *clsx++;
+ while (county--)
+ *newlist++ = *clsy++;
+
+ /* replace the list in dest with the result list */
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ dest->merged = result;
+
+ return true;
+}
+
+/* typeinfo_merge_nonarrays ****************************************************
+
+ Merge two non-array types.
+
+ IN:
+ x................the first type
+ y................the second type
+ mergedx..........merged list of the first type, may be NULL
+ mergedy..........merged list of the descond type, may be NULL
+
+ OUT:
+ *dest............receives the resulting merged list
+ *result..........receives the resulting type
+
+ RETURN VALUE:
+ typecheck_TRUE...*dest has been modified
+ typecheck_FALSE..*dest has not been modified
+ typecheck_FAIL...an exception has been thrown
+
+ NOTE:
+ RESULT is an extra parameter so it can point to dest->typeclass or to
+ dest->elementclass.
+
+*******************************************************************************/
+
+static typecheck_result
+typeinfo_merge_nonarrays(typeinfo_t *dest,
+ classref_or_classinfo *result,
+ classref_or_classinfo x,classref_or_classinfo y,
+ typeinfo_mergedlist_t *mergedx,
+ typeinfo_mergedlist_t *mergedy)
+{
+ classref_or_classinfo t;
+ classinfo *tcls,*common;
+ typeinfo_mergedlist_t *tmerged;
+ bool changed;
+ typecheck_result r;
+ utf *xname;
+ utf *yname;
+
+ TYPEINFO_ASSERT(dest && result && x.any && y.any);
+ TYPEINFO_ASSERT(x.cls != pseudo_class_Null);
+ TYPEINFO_ASSERT(y.cls != pseudo_class_Null);
+ TYPEINFO_ASSERT(x.cls != pseudo_class_New);
+ TYPEINFO_ASSERT(y.cls != pseudo_class_New);
+
+ /*--------------------------------------------------*/
+ /* common cases */
+ /*--------------------------------------------------*/
+
+ /* Common case 1: x and y are the same class or class reference */
+ /* (This case is very simple unless *both* x and y really represent
+ * merges of subclasses of clsx==clsy.)
+ */
+ if ( (x.any == y.any) && (!mergedx || !mergedy) ) {
+ return_simple_x:
+ /* DEBUG */ /* log_text("return simple x"); */
+ changed = (dest->merged != NULL);
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ dest->merged = NULL;
+ *result = x;
+ /* DEBUG */ /* log_text("returning"); */
+ return (typecheck_result) changed;
+ }
+
+ xname = (IS_CLASSREF(x)) ? x.ref->name : x.cls->name;
+ yname = (IS_CLASSREF(y)) ? y.ref->name : y.cls->name;
+
+ /* Common case 2: xname == yname, at least one unresolved */
+ if ((IS_CLASSREF(x) || IS_CLASSREF(y)) && (xname == yname))
+ {
+ /* use the loaded one if any */
+ if (!IS_CLASSREF(y))
+ x = y;
+ goto return_simple_x;
+ }
+
+ /*--------------------------------------------------*/
+ /* non-trivial cases */
+ /*--------------------------------------------------*/
+
+#ifdef TYPEINFO_VERBOSE
+ {
+ typeinfo_t dbgx,dbgy;
+ fprintf(stderr,"merge_nonarrays:\n");
+ fprintf(stderr," ");if(IS_CLASSREF(x))fprintf(stderr,"<ref>");utf_fprint_printable_ascii(stderr,xname);fprintf(stderr,"\n");
+ fprintf(stderr," ");if(IS_CLASSREF(y))fprintf(stderr,"<ref>");utf_fprint_printable_ascii(stderr,yname);fprintf(stderr,"\n");
+ fflush(stderr);
+ typeinfo_init_class(&dbgx,x);
+ dbgx.merged = mergedx;
+ typeinfo_init_class(&dbgy,y);
+ dbgy.merged = mergedy;
+ typeinfo_print(stderr,&dbgx,4);
+ fprintf(stderr," with:\n");
+ typeinfo_print(stderr,&dbgy,4);
+ }
+#endif
+
+ TYPEINFO_ASSERT(IS_CLASSREF(x) || (x.cls->state & CLASS_LOADED));
+ TYPEINFO_ASSERT(IS_CLASSREF(y) || (y.cls->state & CLASS_LOADED));
+
+ /* If y is unresolved or an interface, swap x and y. */
+ if (IS_CLASSREF(y) || (!IS_CLASSREF(x) && y.cls->flags & ACC_INTERFACE))
+ {
+ t = x; x = y; y = t;
+ tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
+ }
+
+ /* {We know: If only one of x,y is unresolved it is x,} */
+ /* { If both x,y are resolved and only one of x,y is an interface it is x.} */
+
+ if (IS_CLASSREF(x)) {
+ /* {We know: x and y have different class names} */
+
+ /* Check if we are merging an unresolved type with java.lang.Object */
+ if (y.cls == class_java_lang_Object && !mergedy) {
+ x = y;
+ goto return_simple_x;
+ }
+
+ common = class_java_lang_Object;
+ goto merge_with_simple_x;
+ }
+
+ /* {We know: both x and y are resolved} */
+ /* {We know: If only one of x,y is an interface it is x.} */
+
+ TYPEINFO_ASSERT(!IS_CLASSREF(x) && !IS_CLASSREF(y));
+ TYPEINFO_ASSERT(x.cls->state & CLASS_LOADED);
+ TYPEINFO_ASSERT(y.cls->state & CLASS_LOADED);
+
+ /* Handle merging of interfaces: */
+ if (x.cls->flags & ACC_INTERFACE) {
+ /* {x.cls is an interface and mergedx == NULL.} */
+
+ if (y.cls->flags & ACC_INTERFACE) {
+ /* We are merging two interfaces. */
+ /* {mergedy == NULL} */
+
+ /* {We know that x.cls!=y.cls (see common case at beginning.)} */
+ result->cls = class_java_lang_Object;
+ return (typecheck_result) typeinfo_merge_two(dest,x,y);
+ }
+
+ /* {We know: x is an interface, y is a class.} */
+
+ /* Check if we are merging an interface with java.lang.Object */
+ if (y.cls == class_java_lang_Object && !mergedy) {
+ x = y;
+ goto return_simple_x;
+ }
+
+ /* If the type y implements x then the result of the merge
+ * is x regardless of mergedy.
+ */
+
+ /* we may have to link the classes */
+ if (!(x.cls->state & CLASS_LINKED))
+ if (!link_class(x.cls))
+ return typecheck_FAIL;
+ if (!(y.cls->state & CLASS_LINKED))
+ if (!link_class(y.cls))
+ return typecheck_FAIL;
+
+ TYPEINFO_ASSERT(x.cls->state & CLASS_LINKED);
+ TYPEINFO_ASSERT(y.cls->state & CLASS_LINKED);
+
+ if (CLASSINFO_IMPLEMENTS_INTERFACE(y.cls,x.cls->index))
+ {
+ /* y implements x, so the result of the merge is x. */
+ goto return_simple_x;
+ }
+
+ r = mergedlist_implements_interface(mergedy,x.cls);
+ if (r == typecheck_FAIL)
+ return r;
+ if (r == typecheck_TRUE)
+ {
+ /* y implements x, so the result of the merge is x. */
+ goto return_simple_x;
+ }
+
+ /* {We know: x is an interface, the type y a class or a merge
+ * of subclasses and is not guaranteed to implement x.} */
+
+ common = class_java_lang_Object;
+ goto merge_with_simple_x;
+ }
+
+ /* {We know: x and y are classes (not interfaces).} */
+
+ /* we may have to link the classes */
+ if (!(x.cls->state & CLASS_LINKED))
+ if (!link_class(x.cls))
+ return typecheck_FAIL;
+ if (!(y.cls->state & CLASS_LINKED))
+ if (!link_class(y.cls))
+ return typecheck_FAIL;
+
+ TYPEINFO_ASSERT(x.cls->state & CLASS_LINKED);
+ TYPEINFO_ASSERT(y.cls->state & CLASS_LINKED);
+
+ /* If *x is deeper in the inheritance hierarchy swap x and y. */
+ if (x.cls->index > y.cls->index) {
+ t = x; x = y; y = t;
+ tmerged = mergedx; mergedx = mergedy; mergedy = tmerged;
+ }
+
+ /* {We know: y is at least as deep in the hierarchy as x.} */
+
+ /* Find nearest common anchestor for the classes. */
+
+ common = x.cls;
+ tcls = y.cls;
+
+ while (tcls->index > common->index)
+ tcls = tcls->super;
+
+ while (common != tcls) {
+ common = common->super;
+ tcls = tcls->super;
+ }
+
+ /* {common == nearest common anchestor of x and y.} */
+
+ /* If x.cls==common and x is a whole class (not a merge of subclasses)
+ * then the result of the merge is x.
+ */
+ if (x.cls == common && !mergedx) {
+ goto return_simple_x;
+ }
+
+ if (mergedx) {
+ result->cls = common;
+ if (mergedy)
+ return (typecheck_result) typeinfo_merge_mergedlists(dest,mergedx,mergedy);
+ else
+ return (typecheck_result) typeinfo_merge_add(dest,mergedx,y);
+ }
+
+merge_with_simple_x:
+ result->cls = common;
+ if (mergedy)
+ return (typecheck_result) typeinfo_merge_add(dest,mergedy,x);
+ else
+ return (typecheck_result) typeinfo_merge_two(dest,x,y);
+}
+
+/* typeinfo_merge **************************************************************
+
+ Merge two types.
+
+ IN:
+ m................method for exception messages
+ dest.............the first type
+ y................the second type
+
+ OUT:
+ *dest............receives the result of the merge
+
+ RETURN VALUE:
+ typecheck_TRUE...*dest has been modified
+ typecheck_FALSE..*dest has not been modified
+ typecheck_FAIL...an exception has been thrown
+
+ PRE-CONDITIONS:
+ 1) *dest must be a valid initialized typeinfo
+ 2) dest != y
+
+*******************************************************************************/
+
+typecheck_result
+typeinfo_merge(methodinfo *m,typeinfo_t *dest,typeinfo_t* y)
+{
+ typeinfo_t *x;
+ typeinfo_t *tmp;
+ classref_or_classinfo common;
+ classref_or_classinfo elementclass;
+ int dimension;
+ int elementtype;
+ bool changed;
+ typecheck_result r;
+
+ /*--------------------------------------------------*/
+ /* fast checks */
+ /*--------------------------------------------------*/
+
+ /* Merging something with itself is a nop */
+ if (dest == y)
+ return typecheck_FALSE;
+
+ /* Merging two returnAddress types is ok. */
+ /* Merging two different returnAddresses never happens, as the verifier */
+ /* keeps them separate in order to check all the possible return paths */
+ /* from JSR subroutines. */
+ if (!dest->typeclass.any && !y->typeclass.any) {
+ TYPEINFO_ASSERT(TYPEINFO_RETURNADDRESS(*dest) == TYPEINFO_RETURNADDRESS(*y));
+ return typecheck_FALSE;
+ }
+
+ /* Primitive types cannot be merged with reference types */
+ /* This must be checked before calls to typeinfo_merge. */
+ TYPEINFO_ASSERT(dest->typeclass.any && y->typeclass.any);
+
+ /* handle uninitialized object types */
+ if (TYPEINFO_IS_NEWOBJECT(*dest) || TYPEINFO_IS_NEWOBJECT(*y)) {
+ if (!TYPEINFO_IS_NEWOBJECT(*dest) || !TYPEINFO_IS_NEWOBJECT(*y)) {
+ typeinfo_merge_error(m,(char*) "Trying to merge uninitialized object type.",dest,y);
+ return typecheck_FAIL;
+ }
+ if (TYPEINFO_NEWOBJECT_INSTRUCTION(*dest) != TYPEINFO_NEWOBJECT_INSTRUCTION(*y)) {
+ typeinfo_merge_error(m,(char*) "Trying to merge different uninitialized objects.",dest,y);
+ return typecheck_FAIL;
+ }
+ /* the same uninitialized object -- no change */
+ return typecheck_FALSE;
+ }
+
+ /*--------------------------------------------------*/
+ /* common cases */
+ /*--------------------------------------------------*/
+
+ /* Common case: dest and y are the same class or class reference */
+ /* (This case is very simple unless *both* dest and y really represent
+ * merges of subclasses of class dest==class y.)
+ */
+ if ((dest->typeclass.any == y->typeclass.any) && (!dest->merged || !y->merged)) {
+return_simple:
+ changed = (dest->merged != NULL);
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ dest->merged = NULL;
+ return (typecheck_result) changed;
+ }
+
+ /* Handle null types: */
+ if (TYPEINFO_IS_NULLTYPE(*y)) {
+ return typecheck_FALSE;
+ }
+ if (TYPEINFO_IS_NULLTYPE(*dest)) {
+ TYPEINFO_FREEMERGED_IF_ANY(dest->merged);
+ TYPEINFO_CLONE(*y,*dest);
+ return typecheck_TRUE;
+ }
+
+ /* Common case: two types with the same name, at least one unresolved */
+ if (IS_CLASSREF(dest->typeclass)) {
+ if (IS_CLASSREF(y->typeclass)) {
+ if (dest->typeclass.ref->name == y->typeclass.ref->name)
+ goto return_simple;
+ }
+ else {
+ /* XXX should we take y instead of dest here? */
+ if (dest->typeclass.ref->name == y->typeclass.cls->name)
+ goto return_simple;
+ }
+ }
+ else {
+ if (IS_CLASSREF(y->typeclass)
+ && (dest->typeclass.cls->name == y->typeclass.ref->name))
+ {
+ goto return_simple;
+ }
+ }
+
+ /*--------------------------------------------------*/
+ /* non-trivial cases */
+ /*--------------------------------------------------*/
+
+#ifdef TYPEINFO_VERBOSE
+ fprintf(stderr,"merge:\n");
+ typeinfo_print(stderr,dest,4);
+ typeinfo_print(stderr,y,4);
+#endif
+
+ /* This function uses x internally, so x and y can be swapped
+ * without changing dest. */
+ x = dest;
+ changed = false;
+
+ /* Handle merging of arrays: */
+ if (TYPEINFO_IS_ARRAY(*x) && TYPEINFO_IS_ARRAY(*y)) {
+
+ /* Make x the one with lesser dimension */
+ if (x->dimension > y->dimension) {
+ tmp = x; x = y; y = tmp;
+ }
+
+ /* If one array (y) has higher dimension than the other,
+ * interpret it as an array (same dim. as x) of Arraystubs. */
+ if (x->dimension < y->dimension) {
+ dimension = x->dimension;
+ elementtype = ARRAYTYPE_OBJECT;
+ elementclass.cls = pseudo_class_Arraystub;
+ }
+ else {
+ dimension = y->dimension;
+ elementtype = y->elementtype;
+ elementclass = y->elementclass;
+ }
+
+ /* {The arrays are of the same dimension.} */
+
+ if (x->elementtype != elementtype) {
+ /* Different element types are merged, so the resulting array
+ * type has one accessible dimension less. */
+ if (--dimension == 0) {
+ common.cls = pseudo_class_Arraystub;
+ elementtype = 0;
+ elementclass.any = NULL;
+ }
+ else {
+ common.cls = class_multiarray_of(dimension,pseudo_class_Arraystub,true);
+ if (!common.cls) {
+ exceptions_throw_internalerror("XXX Coult not create array class");
+ return typecheck_FAIL;
+ }
+
+ elementtype = ARRAYTYPE_OBJECT;
+ elementclass.cls = pseudo_class_Arraystub;
+ }
+ }
+ else {
+ /* {The arrays have the same dimension and elementtype.} */
+
+ if (elementtype == ARRAYTYPE_OBJECT) {
+ /* The elements are references, so their respective
+ * types must be merged.
+ */
+ r = typeinfo_merge_nonarrays(dest,
+ &elementclass,
+ x->elementclass,
+ elementclass,
+ x->merged,y->merged);
+ TYPEINFO_ASSERT(r != typecheck_MAYBE);
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+
+ /* DEBUG */ /* log_text("finding resulting array class: "); */
+ if (IS_CLASSREF(elementclass))
+ common.ref = class_get_classref_multiarray_of(dimension,elementclass.ref);
+ else {
+ common.cls = class_multiarray_of(dimension,elementclass.cls,true);
+ if (!common.cls) {
+ exceptions_throw_internalerror("XXX Coult not create array class");
+ return typecheck_FAIL;
+ }
+ }
+ /* DEBUG */ /* utf_display_printable_ascii(common->name); printf("\n"); */
+ }
+ else {
+ common.any = y->typeclass.any;
+ }
+ }
+ }
+ else {
+ /* {We know that at least one of x or y is no array, so the
+ * result cannot be an array.} */
+
+ r = typeinfo_merge_nonarrays(dest,
+ &common,
+ x->typeclass,y->typeclass,
+ x->merged,y->merged);
+ TYPEINFO_ASSERT(r != typecheck_MAYBE);
+ if (r == typecheck_FAIL)
+ return r;
+ changed |= r;
+
+ dimension = 0;
+ elementtype = 0;
+ elementclass.any = NULL;
+ }
+
+ /* Put the new values into dest if neccessary. */
+
+ if (dest->typeclass.any != common.any) {
+ dest->typeclass.any = common.any;
+ changed = true;
+ }
+ if (dest->dimension != dimension) {
+ dest->dimension = dimension;
+ changed = true;
+ }
+ if (dest->elementtype != elementtype) {
+ dest->elementtype = elementtype;
+ changed = true;
+ }
+ if (dest->elementclass.any != elementclass.any) {
+ dest->elementclass.any = elementclass.any;
+ changed = true;
+ }
+
+ return (typecheck_result) changed;
+}
+#endif /* ENABLE_VERIFER */
+
+
+/**********************************************************************/
+/* DEBUGGING HELPERS */
+/**********************************************************************/
+
+#ifdef TYPEINFO_DEBUG
+
+#if 0
+static int
+typeinfo_test_compare(classref_or_classinfo *a,classref_or_classinfo *b)
+{
+ if (a->any == b->any) return 0;
+ if (a->any < b->any) return -1;
+ return +1;
+}
+
+static void
+typeinfo_test_parse(typeinfo_t *info,char *str)
+{
+ int num;
+ int i;
+ typeinfo_t *infobuf;
+ u1 *typebuf;
+ int returntype;
+ utf *desc = utf_new_char(str);
+
+ num = typeinfo_count_method_args(desc,false);
+ if (num) {
+ typebuf = (u1*) DumpMemory::allocate(sizeof(u1) * num);
+ infobuf = (typeinfo_t*) DumpMemory::allocate(sizeof(typeinfo_t) * num);
+
+ typeinfo_init_from_method_args(desc,typebuf,infobuf,num,false,
+ &returntype,info);
+
+ TYPEINFO_ALLOCMERGED(info->merged,num);
+ info->merged->count = num;
+
+ for (i=0; i<num; ++i) {
+ if (typebuf[i] != TYPE_ADR) {
+ log_text("non-reference type in mergedlist");
+ assert(0);
+ }
+
+ info->merged->list[i].any = infobuf[i].typeclass.any;
+ }
+ qsort(info->merged->list,num,sizeof(classref_or_classinfo),
+ (int(*)(const void *,const void *))&typeinfo_test_compare);
+ }
+ else {
+ typeinfo_init_from_method_args(desc,NULL,NULL,0,false,
+ &returntype,info);
+ }
+}
+#endif
+
+#define TYPEINFO_TEST_BUFLEN 4000
+
+static bool
+typeinfo_equal(typeinfo_t *x,typeinfo_t *y)
+{
+ int i;
+
+ if (x->typeclass.any != y->typeclass.any) return false;
+ if (x->dimension != y->dimension) return false;
+ if (x->dimension) {
+ if (x->elementclass.any != y->elementclass.any) return false;
+ if (x->elementtype != y->elementtype) return false;
+ }
+
+ if (TYPEINFO_IS_NEWOBJECT(*x))
+ if (TYPEINFO_NEWOBJECT_INSTRUCTION(*x)
+ != TYPEINFO_NEWOBJECT_INSTRUCTION(*y))
+ return false;
+
+ if (x->merged || y->merged) {
+ if (!(x->merged && y->merged)) return false;
+ if (x->merged->count != y->merged->count) return false;
+ for (i=0; i<x->merged->count; ++i)
+ if (x->merged->list[i].any != y->merged->list[i].any)
+ return false;
+ }
+ return true;
+}
+
+static void
+typeinfo_testmerge(typeinfo_t *a,typeinfo_t *b,typeinfo_t *result,int *failed)
+{
+ typeinfo_t dest;
+ bool changed,changed_should_be;
+ typecheck_result r;
+
+ TYPEINFO_CLONE(*a,dest);
+
+ printf("\n ");
+ typeinfo_print_short(stdout,&dest);
+ printf("\n ");
+ typeinfo_print_short(stdout,b);
+ printf("\n");
+
+ r = typeinfo_merge(NULL,&dest,b);
+ if (r == typecheck_FAIL) {
+ printf("EXCEPTION\n");
+ return;
+ }
+ changed = (r) ? 1 : 0;
+ changed_should_be = (!typeinfo_equal(&dest,a)) ? 1 : 0;
+
+ printf(" %s\n",(changed) ? "changed" : "=");
+
+ if (typeinfo_equal(&dest,result)) {
+ printf("OK ");
+ typeinfo_print_short(stdout,&dest);
+ printf("\n");
+ if (changed != changed_should_be) {
+ printf("WRONG RETURN VALUE!\n");
+ (*failed)++;
+ }
+ }
+ else {
+ printf("RESULT ");
+ typeinfo_print_short(stdout,&dest);
+ printf("\n");
+ printf("SHOULD BE ");
+ typeinfo_print_short(stdout,result);
+ printf("\n");
+ (*failed)++;
+ }
+}
+
+#if 0
+static void
+typeinfo_inc_dimension(typeinfo_t *info)
+{
+ if (info->dimension++ == 0) {
+ info->elementtype = ARRAYTYPE_OBJECT;
+ info->elementclass = info->typeclass;
+ }
+ info->typeclass = class_array_of(info->typeclass,true);
+}
+#endif
+
+#define TYPEINFO_TEST_MAXDIM 10
+
+static void
+typeinfo_testrun(char *filename)
+{
+ char buf[TYPEINFO_TEST_BUFLEN];
+ char bufa[TYPEINFO_TEST_BUFLEN];
+ char bufb[TYPEINFO_TEST_BUFLEN];
+ char bufc[TYPEINFO_TEST_BUFLEN];
+ typeinfo_t a,b,c;
+ int maxdim;
+ int failed = 0;
+ FILE *file = fopen(filename,"rt");
+ int res;
+
+ if (!file) {
+ log_text("could not open typeinfo test file");
+ assert(0);
+ }
+
+ while (fgets(buf,TYPEINFO_TEST_BUFLEN,file)) {
+ if (buf[0] == '#' || !strlen(buf))
+ continue;
+
+ res = sscanf(buf,"%s\t%s\t%s\n",bufa,bufb,bufc);
+ if (res != 3 || !strlen(bufa) || !strlen(bufb) || !strlen(bufc)) {
+ log_text("Invalid line in typeinfo test file (none of empty, comment or test)");
+ assert(0);
+ }
+
+#if 0
+ typeinfo_test_parse(&a,bufa);
+ typeinfo_test_parse(&b,bufb);
+ typeinfo_test_parse(&c,bufc);
+#endif
+#if 0
+ do {
+#endif
+ typeinfo_testmerge(&a,&b,&c,&failed); /* check result */
+ typeinfo_testmerge(&b,&a,&c,&failed); /* check commutativity */
+
+ if (TYPEINFO_IS_NULLTYPE(a)) break;
+ if (TYPEINFO_IS_NULLTYPE(b)) break;
+ if (TYPEINFO_IS_NULLTYPE(c)) break;
+
+ maxdim = a.dimension;
+ if (b.dimension > maxdim) maxdim = b.dimension;
+ if (c.dimension > maxdim) maxdim = c.dimension;
+
+#if 0
+ if (maxdim < TYPEINFO_TEST_MAXDIM) {
+ typeinfo_inc_dimension(&a);
+ typeinfo_inc_dimension(&b);
+ typeinfo_inc_dimension(&c);
+ }
+ } while (maxdim < TYPEINFO_TEST_MAXDIM);
+#endif
+ }
+
+ fclose(file);
+
+ if (failed) {
+ fprintf(stderr,"Failed typeinfo_merge tests: %d\n",failed);
+ log_text("Failed test");
+ assert(0);
+ }
+}
+
+void
+typeinfo_test()
+{
+ log_text("Running typeinfo test file...");
+ typeinfo_testrun("typeinfo.tst");
+ log_text("Finished typeinfo test file.");
+}
+
+#if 0
+void
+typeinfo_init_from_fielddescriptor(typeinfo_t *info,char *desc)
+{
+ typeinfo_init_from_descriptor(info,desc,desc+strlen(desc));
+}
+#endif
+
+#define TYPEINFO_MAXINDENT 80
+
+void
+typeinfo_print_class(FILE *file,classref_or_classinfo c)
+{
+ /*fprintf(file,"<class %p>",c.any);*/
+
+ if (!c.any) {
+ fprintf(file,"<null>");
+ }
+ else {
+ if (IS_CLASSREF(c)) {
+ fprintf(file,"<ref>");
+ utf_fprint_printable_ascii(file,c.ref->name);
+ }
+ else {
+ utf_fprint_printable_ascii(file,c.cls->name);
+ }
+ }
+}
+
+void
+typeinfo_print(FILE *file,typeinfo_t *info,int indent)
+{
+ int i;
+ char ind[TYPEINFO_MAXINDENT + 1];
+ instruction *ins;
+ basicblock *bptr;
+
+ if (indent > TYPEINFO_MAXINDENT) indent = TYPEINFO_MAXINDENT;
+
+ for (i=0; i<indent; ++i)
+ ind[i] = ' ';
+ ind[i] = (char) 0;
+
+ if (TYPEINFO_IS_PRIMITIVE(*info)) {
+ bptr = (basicblock*) TYPEINFO_RETURNADDRESS(*info);
+ if (bptr)
+ fprintf(file,"%sreturnAddress (L%03d)\n",ind,bptr->nr);
+ else
+ fprintf(file,"%sprimitive\n",ind);
+ return;
+ }
+
+ if (TYPEINFO_IS_NULLTYPE(*info)) {
+ fprintf(file,"%snull\n",ind);
+ return;
+ }
+
+ if (TYPEINFO_IS_NEWOBJECT(*info)) {
+ ins = (instruction *) TYPEINFO_NEWOBJECT_INSTRUCTION(*info);
+ if (ins) {
+ fprintf(file,"%sNEW(%p):",ind,(void*)ins);
+ typeinfo_print_class(file,ins[-1].sx.val.c);
+ fprintf(file,"\n");
+ }
+ else {
+ fprintf(file,"%sNEW(this)",ind);
+ }
+ return;
+ }
+
+ fprintf(file,"%sClass: ",ind);
+ typeinfo_print_class(file,info->typeclass);
+ fprintf(file,"\n");
+
+ if (TYPEINFO_IS_ARRAY(*info)) {
+ fprintf(file,"%sDimension: %d",ind,(int)info->dimension);
+ fprintf(file,"\n%sElements: ",ind);
+ switch (info->elementtype) {
+ case ARRAYTYPE_INT : fprintf(file,"int\n"); break;
+ case ARRAYTYPE_LONG : fprintf(file,"long\n"); break;
+ case ARRAYTYPE_FLOAT : fprintf(file,"float\n"); break;
+ case ARRAYTYPE_DOUBLE : fprintf(file,"double\n"); break;
+ case ARRAYTYPE_BYTE : fprintf(file,"byte\n"); break;
+ case ARRAYTYPE_CHAR : fprintf(file,"char\n"); break;
+ case ARRAYTYPE_SHORT : fprintf(file,"short\n"); break;
+ case ARRAYTYPE_BOOLEAN : fprintf(file,"boolean\n"); break;
+
+ case ARRAYTYPE_OBJECT:
+ typeinfo_print_class(file,info->elementclass);
+ fprintf(file,"\n");
+ break;
+
+ default:
+ fprintf(file,"INVALID ARRAYTYPE!\n");
+ }
+ }
+
+ if (info->merged) {
+ fprintf(file,"%sMerged: ",ind);
+ for (i=0; i<info->merged->count; ++i) {
+ if (i) fprintf(file,", ");
+ typeinfo_print_class(file,info->merged->list[i]);
+ }
+ fprintf(file,"\n");
+ }
+}
+
+void
+typeinfo_print_short(FILE *file,typeinfo_t *info)
+{
+ int i;
+ instruction *ins;
+ basicblock *bptr;
+
+ /*fprintf(file,"<typeinfo %p>",info);*/
+
+ if (!info) {
+ fprintf(file,"(typeinfo*)NULL");
+ return;
+ }
+
+ if (TYPEINFO_IS_PRIMITIVE(*info)) {
+ bptr = (basicblock*) TYPEINFO_RETURNADDRESS(*info);
+ if (bptr)
+ fprintf(file,"ret(L%03d)",bptr->nr);
+ else
+ fprintf(file,"primitive");
+ return;
+ }
+
+ if (TYPEINFO_IS_NULLTYPE(*info)) {
+ fprintf(file,"null");
+ return;
+ }
+
+ if (TYPEINFO_IS_NEWOBJECT(*info)) {
+ ins = (instruction *) TYPEINFO_NEWOBJECT_INSTRUCTION(*info);
+ if (ins) {
+ /*fprintf(file,"<ins %p>",ins);*/
+ fprintf(file,"NEW(%p):",(void*)ins);
+ typeinfo_print_class(file,ins[-1].sx.val.c);
+ }
+ else
+ fprintf(file,"NEW(this)");
+ return;
+ }
+
+ typeinfo_print_class(file,info->typeclass);
+
+ if (info->merged) {
+ fprintf(file,"{");
+ for (i=0; i<info->merged->count; ++i) {
+ if (i) fprintf(file,",");
+ typeinfo_print_class(file,info->merged->list[i]);
+ }
+ fprintf(file,"}");
+ }
+}
+
+void
+typeinfo_print_type(FILE *file,int type,typeinfo_t *info)
+{
+ switch (type) {
+ case TYPE_VOID: fprintf(file,"V"); break;
+ case TYPE_INT: fprintf(file,"I"); break;
+ case TYPE_FLT: fprintf(file,"F"); break;
+ case TYPE_DBL: fprintf(file,"D"); break;
+ case TYPE_LNG: fprintf(file,"J"); break;
+ case TYPE_RET: fprintf(file,"R:"); /* FALLTHROUGH! */
+ case TYPE_ADR:
+ typeinfo_print_short(file,info);
+ break;
+
+ default:
+ fprintf(file,"!");
+ }
+}
+
+void
+typedescriptor_print(FILE *file,typedescriptor_t *td)
+{
+ typeinfo_print_type(file,td->type,&(td->typeinfo));
+}
+
+void
+typevector_print(FILE *file,varinfo *vec,int size)
+{
+ int i;
+
+ for (i=0; i<size; ++i) {
+ fprintf(file," %d=",i);
+ typeinfo_print_type(file, vec[i].type, &(vec[i].typeinfo));
+ }
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* TYPEINFO_DEBUG */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/jit/verify/typeinfo.h - type system used by the type checker
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-#ifndef _TYPEINFO_H
-#define _TYPEINFO_H
-
-/* resolve typedef cycles *****************************************************/
-
-typedef struct typeinfo typeinfo_t;
-typedef struct typeinfo_mergedlist typeinfo_mergedlist_t;
-typedef struct typedescriptor typedescriptor_t;
-
-#include "config.h"
-#include "vm/types.h"
-
-#include "vm/global.h"
-#include "vm/references.h"
-
-
-/* configuration **************************************************************/
-
-/*
- * TYPECHECK_STATISTICS activates gathering statistical information.
- * TYPEINFO_DEBUG activates debug checks and debug helpers in typeinfo.c
- * TYPECHECK_DEBUG activates debug checks in typecheck.c
- * TYPEINFO_DEBUG_TEST activates the typeinfo test at startup.
- * TYPECHECK_VERBOSE_IMPORTANT activates important debug messages
- * TYPECHECK_VERBOSE activates all debug messages
- * TYPEINFO_VERBOSE activates debug prints in typeinfo.c
- */
-#ifdef ENABLE_VERIFIER
-#ifndef NDEBUG
-/*#define TYPECHECK_STATISTICS*/
-#define TYPEINFO_DEBUG
-/*#define TYPEINFO_VERBOSE*/
-#define TYPECHECK_DEBUG
-/*#define TYPEINFO_DEBUG_TEST*/
-/*#define TYPECHECK_VERBOSE*/
-/*#define TYPECHECK_VERBOSE_IMPORTANT*/
-#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
-#define TYPECHECK_VERBOSE_OPT
-#endif
-#endif
-#endif
-
-#ifdef TYPECHECK_VERBOSE_OPT
-extern bool opt_typecheckverbose;
-#endif
-
-/* types **********************************************************************/
-
-/* typecheck_result - return type for boolean and tristate functions */
-/* which may also throw exceptions (typecheck_FAIL). */
-
-/* NOTE: Use the enum values, not the uppercase #define macros! */
-#define TYPECHECK_MAYBE 0x02
-#define TYPECHECK_FAIL 0x04
-
-typedef enum {
- typecheck_FALSE = false,
- typecheck_TRUE = true,
- typecheck_MAYBE = TYPECHECK_MAYBE,
- typecheck_FAIL = TYPECHECK_FAIL
-} typecheck_result;
-
-/* check that typecheck_MAYBE is not ambiguous */
-#if TYPECHECK_MAYBE == true
-#error "`typecheck_MAYBE` must not be the same as `true`"
-#endif
-#if TYPECHECK_MAYBE == false
-#error "`typecheck_MAYBE` must not be the same as `false`"
-#endif
-
-/* check that typecheck_FAIL is not ambiguous */
-#if (true & TYPECHECK_FAIL) != 0
-#error "`true` must not have bit 0x02 set (conflicts with typecheck_FAIL)"
-#endif
-
-/* data structures for the type system ****************************************/
-
-/* The typeinfo structure stores detailed information on address types.
- * (stack elements, variables, etc. with type == TYPE_ADR.)
- *
- * There are two kinds of address types which can be distinguished by
- * the value of the typeclass field:
- *
- * 1) typeclass == NULL: returnAddress type
- * use TYPEINFO_IS_PRIMITIVE to test for this
- *
- * 2) typeclass != NULL: reference type
- * use TYPEINFO_IS_REFERENCE to test for this
- *
- * Note: For non-address types either there is no typeinfo allocated
- * or the fields of the typeinfo struct contain undefined values!
- * DO NOT access the typeinfo for non-address types!
- *
- * CAUTION: The typeinfo structure should be considered opaque outside of
- * typeinfo.[ch]. Please use the macros and functions defined here to
- * access typeinfo structures!
- */
-
-/* At all times *exactly one* of the following conditions is true for
- * a particular typeinfo struct:
- *
- * A) typeclass == NULL
- *
- * This is a returnAddress type.
- *
- * Use TYPEINFO_IS_PRIMITIVE to check for this.
- * Use TYPEINFO_RETURNADDRESS to access the pointer in elementclass.
- * Don't access other fields of the struct.
- *
- * B) typeclass == pseudo_class_Null
- *
- * This is the null-reference type.
- * Use TYPEINFO_IS_NULLTYPE to check for this.
- * Don't access other fields of the struct.
- *
- * C) typeclass == pseudo_class_New
- *
- * This is an 'uninitialized object' type. elementclass can be
- * cast to instruction* and points to the NEW instruction
- * responsible for creating this type.
- *
- * Use TYPEINFO_NEWOBJECT_INSTRUCTION to access the pointer in
- * elementclass.
- * Don't access other fields of the struct.
- *
- * D) typeclass == pseudo_class_Arraystub
- *
- * This type is used to represent the result of merging array types
- * with incompatible component types. An arraystub allows no access
- * to its components (since their type is undefined), but it allows
- * operations which act directly on an arbitrary array type (such as
- * requesting the array size).
- *
- * NOTE: An array stub does *not* count as an array. It has dimension
- * zero.
- *
- * Otherwise like a normal class reference type.
- * Don't access other fields of the struct.
- *
- * E) typeclass is an array class
- *
- * An array reference.
- * elementclass...typeclass of the element type
- * dimension......dimension of the array (>=1)
- * elementtype....element type (ARRAYTYPE_...)
- * merged.........mergedlist of the element type
- *
- * Use TYPEINFO_IS_ARRAY to check for this case.
- *
- * The elementclass may be one of the following:
- * 1) pseudo_class_Arraystub
- * 2) an unresolved type
- * 3) a loaded interface
- * 4) a loaded (non-pseudo-,non-array-)class != (BOOTSTRAP)java.lang.Object
- * Note: `merged` may be used
- * 5) (BOOTSTRAP)java.lang.Object
- * Note: `merged` may be used
- *
- * For the semantics of the merged field in cases 4) and 5) consult the
- * corresponding descriptions with `elementclass` replaced by `typeclass`.
- *
- * F) typeclass is an unresolved type (a symbolic class/interface reference)
- *
- * The type has not been resolved yet. (Meaning it corresponds to an
- * unloaded class or interface).
- * Don't access other fields of the struct.
- *
- * G) typeclass is a loaded interface
- *
- * An interface reference type.
- * Don't access other fields of the struct.
- *
- * H) typeclass is a loaded (non-pseudo-,non-array-)class != (BOOTSTRAP)java.lang.Object
- *
- * A loaded class type.
- * All classref_or_classinfos in u.merged.list (if any) are
- * loaded subclasses of typeclass (no interfaces, array classes, or
- * unresolved types).
- * Don't access other fields of the struct.
- *
- * I) typeclass is (BOOTSTRAP)java.lang.Object
- *
- * The most general kind of reference type.
- * In this case u.merged.count and u.merged.list
- * are valid and may be non-zero.
- * The classref_or_classinfos in u.merged.list (if any) may be
- * classes, interfaces, pseudo classes or unresolved types.
- * Don't access other fields of the struct.
- */
-
-/* The following algorithm is used to determine if the type described
- * by this typeinfo struct supports the interface X: * XXX add MAYBE *
- *
- * 1) If typeclass is X or a subinterface of X the answer is "yes".
- * 2) If typeclass is a (pseudo) class implementing X the answer is "yes".
- * 3) If typeclass is not an array and u.merged.count>0
- * and all classes/interfaces in u.merged.list implement X
- * the answer is "yes".
- * 4) If none of the above is true the answer is "no".
- */
-
-/*
- * CAUTION: The typeinfo structure should be considered opaque outside of
- * typeinfo.[ch]. Please use the macros and functions defined here to
- * access typeinfo structures!
- */
-struct typeinfo {
- classref_or_classinfo typeclass;
- classref_or_classinfo elementclass; /* valid if dimension>0 */ /* various uses! */
- typeinfo_mergedlist_t *merged;
- u1 dimension;
- u1 elementtype; /* valid if dimension>0 */
-};
-
-struct typeinfo_mergedlist {
- s4 count;
- classref_or_classinfo list[1]; /* variable length! */
-};
-
-/* a type descriptor stores a basic type and the typeinfo */
-/* this is used for storing the type of a local variable, and for */
-/* storing types in the signature of a method */
-
-struct typedescriptor {
- typeinfo_t typeinfo; /* valid if type == TYPE_ADR */
- u1 type; /* basic type (TYPE_INT, ...) */
-};
-
-/****************************************************************************/
-/* MACROS */
-/****************************************************************************/
-
-/* NOTE: The TYPEINFO macros take typeinfo *structs*, not pointers as
- * arguments. You have to dereference any pointers.
- */
-
-/* typevectors **************************************************************/
-
-#define TYPEVECTOR_SIZE(size) \
- ((size) * sizeof(varinfo))
-
-#define DNEW_TYPEVECTOR(size) \
- ((varinfo *) DMNEW(uint8_t, TYPEVECTOR_SIZE(size)))
-
-#define DMNEW_TYPEVECTOR(num,size) \
- ((void *) DMNEW(uint8_t, (num) * TYPEVECTOR_SIZE(size)))
-
-#define MGET_TYPEVECTOR(array,index,size) \
- ((varinfo*) (((u1*)(array)) + TYPEVECTOR_SIZE(size) * (index)))
-
-/* internally used macros ***************************************************/
-
-/* internal, don't use this explicitly! */
-#define TYPEINFO_ALLOCMERGED(mergedlist,count) \
- do {(mergedlist) = (typeinfo_mergedlist_t *) DMNEW(uint8_t, \
- sizeof(typeinfo_mergedlist_t) \
- + ((count)-1)*sizeof(classinfo*));} while(0)
-
-/* internal, don't use this explicitly! */
-#define TYPEINFO_FREEMERGED(mergedlist)
-
-/* internal, don't use this explicitly! */
-#define TYPEINFO_FREEMERGED_IF_ANY(mergedlist)
-
-/* macros for type queries **************************************************/
-
-#define TYPEINFO_IS_PRIMITIVE(info) \
- ((info).typeclass.any == NULL)
-
-#define TYPEINFO_IS_REFERENCE(info) \
- ((info).typeclass.any != NULL)
-
-#define TYPEINFO_IS_NULLTYPE(info) \
- ((info).typeclass.cls == pseudo_class_Null)
-
-#define TYPEINFO_IS_NEWOBJECT(info) \
- ((info).typeclass.cls == pseudo_class_New)
-
-#define TYPEINFO_IS_JAVA_LANG_CLASS(info) \
- ((info).typeclass.cls == class_java_lang_Class)
-
-/* only use this if TYPEINFO_IS_PRIMITIVE returned true! */
-#define TYPEINFO_RETURNADDRESS(info) \
- ((info).elementclass.any)
-
-/* only use this if TYPEINFO_IS_NEWOBJECT returned true! */
-#define TYPEINFO_NEWOBJECT_INSTRUCTION(info) \
- ((info).elementclass.any)
-
-/* only use this if TYPEINFO_IS_JAVA_LANG_CLASS returned true! */
-#define TYPEINFO_JAVA_LANG_CLASS_CLASSREF(info) \
- ((info).elementclass.ref)
-
-/* macros for array type queries ********************************************/
-
-#define TYPEINFO_IS_ARRAY(info) \
- ( TYPEINFO_IS_REFERENCE(info) \
- && ((info).dimension != 0) )
-
-#define TYPEINFO_IS_SIMPLE_ARRAY(info) \
- ( ((info).dimension == 1) )
-
-#define TYPEINFO_IS_ARRAY_ARRAY(info) \
- ( ((info).dimension >= 2) )
-
-#define TYPEINFO_IS_PRIMITIVE_ARRAY(info,arraytype) \
- ( TYPEINFO_IS_SIMPLE_ARRAY(info) \
- && ((info).elementtype == (arraytype)) )
-
-#define TYPEINFO_IS_OBJECT_ARRAY(info) \
- ( TYPEINFO_IS_SIMPLE_ARRAY(info) \
- && ((info).elementclass.any != NULL) )
-
-/* assumes that info describes an array type */
-#define TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info) \
- ( ((info).elementclass.any != NULL) \
- || ((info).dimension >= 2) )
-
-#define TYPEINFO_IS_ARRAY_OF_REFS(info) \
- ( TYPEINFO_IS_ARRAY(info) \
- && TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info) )
-
-#define TYPE_IS_RETURNADDRESS(type,info) \
- ( ((type)==TYPE_RET) \
- && TYPEINFO_IS_PRIMITIVE(info) )
-
-#define TYPE_IS_REFERENCE(type,info) \
- ( ((type)==TYPE_ADR) \
- && !TYPEINFO_IS_PRIMITIVE(info) )
-
-#define TYPEDESC_IS_RETURNADDRESS(td) \
- TYPE_IS_RETURNADDRESS((td).type,(td).typeinfo)
-
-#define TYPEDESC_IS_REFERENCE(td) \
- TYPE_IS_REFERENCE((td).type,(td).typeinfo)
-
-/* queries allowing the null type ********************************************/
-
-#define TYPEINFO_MAYBE_ARRAY(info) \
- (TYPEINFO_IS_ARRAY(info) || TYPEINFO_IS_NULLTYPE(info))
-
-#define TYPEINFO_MAYBE_PRIMITIVE_ARRAY(info,at) \
- (TYPEINFO_IS_PRIMITIVE_ARRAY(info,at) || TYPEINFO_IS_NULLTYPE(info))
-
-#define TYPEINFO_MAYBE_ARRAY_OF_REFS(info) \
- (TYPEINFO_IS_ARRAY_OF_REFS(info) || TYPEINFO_IS_NULLTYPE(info))
-
-/* macros for initializing typeinfo structures ******************************/
-
-#define TYPEINFO_INIT_PRIMITIVE(info) \
- do {(info).typeclass.any = NULL; \
- (info).elementclass.any = NULL; \
- (info).merged = NULL; \
- (info).dimension = 0; \
- (info).elementtype = 0;} while(0)
-
-#define TYPEINFO_INIT_RETURNADDRESS(info,adr) \
- do {(info).typeclass.any = NULL; \
- (info).elementclass.any = (adr); \
- (info).merged = NULL; \
- (info).dimension = 0; \
- (info).elementtype = 0;} while(0)
-
-#define TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,cinfo) \
- do {(info).typeclass.cls = (cinfo); \
- (info).elementclass.any = NULL; \
- (info).merged = NULL; \
- (info).dimension = 0; \
- (info).elementtype = 0;} while(0)
-
-#define TYPEINFO_INIT_JAVA_LANG_CLASS(info,c) \
- do {(info).typeclass.any = class_java_lang_Class; \
- (info).elementclass = (c); \
- (info).merged = NULL; \
- (info).dimension = 0; \
- (info).elementtype = 0;} while(0)
-
-#define TYPEINFO_INIT_NULLTYPE(info) \
- TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,pseudo_class_Null)
-
-#define TYPEINFO_INIT_NEWOBJECT(info,instr) \
- do {(info).typeclass.cls = pseudo_class_New; \
- (info).elementclass.any = (instr); \
- (info).merged = NULL; \
- (info).dimension = 0; \
- (info).elementtype = 0;} while(0)
-
-#define TYPEINFO_INIT_PRIMITIVE_ARRAY(info,arraytype) \
- typeinfo_init_classinfo(&(info),primitivetype_table[arraytype].arrayclass);
-
-/* macros for copying types (destinition is not checked or freed) ***********/
-
-/* TYPEINFO_COPY makes a shallow copy, the merged pointer is simply copied. */
-#define TYPEINFO_COPY(src,dst) \
- do {(dst) = (src);} while(0)
-
-/* TYPEINFO_CLONE makes a deep copy, the merged list (if any) is duplicated
- * into a newly allocated array.
- */
-#define TYPEINFO_CLONE(src,dst) \
- do {(dst) = (src); \
- if ((dst).merged) typeinfo_clone(&(src),&(dst));} while(0)
-
-/****************************************************************************/
-/* FUNCTIONS */
-/****************************************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* typevector functions *****************************************************/
-
-/* element read-only access */
-bool typevector_checktype(varinfo *set,int index,int type);
-bool typevector_checkreference(varinfo *set,int index);
-bool typevector_checkretaddr(varinfo *set,int index);
-
-/* element write access */
-void typevector_store(varinfo *set,int index,int type,typeinfo_t *info);
-void typevector_store_retaddr(varinfo *set,int index,typeinfo_t *info);
-bool typevector_init_object(varinfo *set,void *ins,classref_or_classinfo initclass,int size);
-
-/* vector functions */
-varinfo *typevector_copy(varinfo *src,int size);
-void typevector_copy_inplace(varinfo *src,varinfo *dst,int size);
-typecheck_result typevector_merge(methodinfo *m,varinfo *dst,varinfo *y,int size);
-
-/* inquiry functions (read-only) ********************************************/
-
-bool typeinfo_is_array(typeinfo_t *info);
-bool typeinfo_is_primitive_array(typeinfo_t *info,int arraytype);
-bool typeinfo_is_array_of_refs(typeinfo_t *info);
-
-typecheck_result typeinfo_is_assignable(typeinfo_t *value,typeinfo_t *dest);
-typecheck_result typeinfo_is_assignable_to_class(typeinfo_t *value,classref_or_classinfo dest);
-
-/* initialization functions *************************************************/
-
-/* RETURN VALUE (bool):
- * true.............ok,
- * false............an exception has been thrown.
- *
- * RETURN VALUE (int):
- * >= 0.............ok,
- * -1...............an exception has been thrown.
- */
-void typeinfo_init_classinfo(typeinfo_t *info,classinfo *c);
-bool typeinfo_init_class(typeinfo_t *info,classref_or_classinfo c);
-bool typeinfo_init_component(typeinfo_t *srcarray,typeinfo_t *dst);
-
-bool typeinfo_init_from_typedesc(typedesc *desc,u1 *type,typeinfo_t *info);
-bool typeinfos_init_from_methoddesc(methoddesc *desc,u1 *typebuf,
- typeinfo_t *infobuf,
- int buflen,bool twoword,
- u1 *returntype,typeinfo_t *returntypeinfo);
-bool typedescriptor_init_from_typedesc(typedescriptor_t *td,
- typedesc *desc);
-bool typeinfo_init_varinfo_from_typedesc(varinfo *var,
- typedesc *desc);
-int typedescriptors_init_from_methoddesc(typedescriptor_t *td,
- methoddesc *desc,
- int buflen,bool twoword,int startindex,
- typedescriptor_t *returntype);
-bool typeinfo_init_varinfos_from_methoddesc(varinfo *vars,
- methoddesc *desc,
- int buflen, int startindex,
- s4 *map,
- typedescriptor_t *returntype);
-
-void typeinfo_clone(typeinfo_t *src,typeinfo_t *dest);
-
-/* freeing memory ***********************************************************/
-
-void typeinfo_free(typeinfo_t *info);
-
-/* functions for merging types **********************************************/
-
-typecheck_result typeinfo_merge(methodinfo *m,typeinfo_t *dest,typeinfo_t* y);
-
-/* debugging helpers ********************************************************/
-
-#ifdef TYPEINFO_DEBUG
-
-#include <stdio.h>
-
-void typeinfo_test();
-void typeinfo_print_class(FILE *file,classref_or_classinfo c);
-void typeinfo_print(FILE *file,typeinfo_t *info,int indent);
-void typeinfo_print_short(FILE *file,typeinfo_t *info);
-void typeinfo_print_type(FILE *file,int type,typeinfo_t *info);
-void typedescriptor_print(FILE *file,typedescriptor_t *td);
-void typevector_print(FILE *file,varinfo *vec,int size);
-
-#endif /* TYPEINFO_DEBUG */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _TYPEINFO_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
--- /dev/null
+/* src/vm/jit/verify/typeinfo.h - type system used by the type checker
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+#ifndef _TYPEINFO_H
+#define _TYPEINFO_H
+
+/* resolve typedef cycles *****************************************************/
+
+typedef struct typeinfo typeinfo_t;
+typedef struct typeinfo_mergedlist typeinfo_mergedlist_t;
+typedef struct typedescriptor typedescriptor_t;
+
+#include "config.h"
+#include "vm/types.h"
+
+#include "vm/global.h"
+#include "vm/references.h"
+
+
+/* configuration **************************************************************/
+
+/*
+ * TYPECHECK_STATISTICS activates gathering statistical information.
+ * TYPEINFO_DEBUG activates debug checks and debug helpers in typeinfo.c
+ * TYPECHECK_DEBUG activates debug checks in typecheck.c
+ * TYPEINFO_DEBUG_TEST activates the typeinfo test at startup.
+ * TYPECHECK_VERBOSE_IMPORTANT activates important debug messages
+ * TYPECHECK_VERBOSE activates all debug messages
+ * TYPEINFO_VERBOSE activates debug prints in typeinfo.c
+ */
+#ifdef ENABLE_VERIFIER
+#ifndef NDEBUG
+/*#define TYPECHECK_STATISTICS*/
+#define TYPEINFO_DEBUG
+/*#define TYPEINFO_VERBOSE*/
+#define TYPECHECK_DEBUG
+/*#define TYPEINFO_DEBUG_TEST*/
+/*#define TYPECHECK_VERBOSE*/
+/*#define TYPECHECK_VERBOSE_IMPORTANT*/
+#if defined(TYPECHECK_VERBOSE) || defined(TYPECHECK_VERBOSE_IMPORTANT)
+#define TYPECHECK_VERBOSE_OPT
+#endif
+#endif
+#endif
+
+#ifdef TYPECHECK_VERBOSE_OPT
+extern bool opt_typecheckverbose;
+#endif
+
+/* types **********************************************************************/
+
+/* typecheck_result - return type for boolean and tristate functions */
+/* which may also throw exceptions (typecheck_FAIL). */
+
+/* NOTE: Use the enum values, not the uppercase #define macros! */
+#define TYPECHECK_MAYBE 0x02
+#define TYPECHECK_FAIL 0x04
+
+typedef enum {
+ typecheck_FALSE = false,
+ typecheck_TRUE = true,
+ typecheck_MAYBE = TYPECHECK_MAYBE,
+ typecheck_FAIL = TYPECHECK_FAIL
+} typecheck_result;
+
+/* check that typecheck_MAYBE is not ambiguous */
+#if TYPECHECK_MAYBE == true
+#error "`typecheck_MAYBE` must not be the same as `true`"
+#endif
+#if TYPECHECK_MAYBE == false
+#error "`typecheck_MAYBE` must not be the same as `false`"
+#endif
+
+/* check that typecheck_FAIL is not ambiguous */
+#if (true & TYPECHECK_FAIL) != 0
+#error "`true` must not have bit 0x02 set (conflicts with typecheck_FAIL)"
+#endif
+
+/* data structures for the type system ****************************************/
+
+/* The typeinfo structure stores detailed information on address types.
+ * (stack elements, variables, etc. with type == TYPE_ADR.)
+ *
+ * There are two kinds of address types which can be distinguished by
+ * the value of the typeclass field:
+ *
+ * 1) typeclass == NULL: returnAddress type
+ * use TYPEINFO_IS_PRIMITIVE to test for this
+ *
+ * 2) typeclass != NULL: reference type
+ * use TYPEINFO_IS_REFERENCE to test for this
+ *
+ * Note: For non-address types either there is no typeinfo allocated
+ * or the fields of the typeinfo struct contain undefined values!
+ * DO NOT access the typeinfo for non-address types!
+ *
+ * CAUTION: The typeinfo structure should be considered opaque outside of
+ * typeinfo.[ch]. Please use the macros and functions defined here to
+ * access typeinfo structures!
+ */
+
+/* At all times *exactly one* of the following conditions is true for
+ * a particular typeinfo struct:
+ *
+ * A) typeclass == NULL
+ *
+ * This is a returnAddress type.
+ *
+ * Use TYPEINFO_IS_PRIMITIVE to check for this.
+ * Use TYPEINFO_RETURNADDRESS to access the pointer in elementclass.
+ * Don't access other fields of the struct.
+ *
+ * B) typeclass == pseudo_class_Null
+ *
+ * This is the null-reference type.
+ * Use TYPEINFO_IS_NULLTYPE to check for this.
+ * Don't access other fields of the struct.
+ *
+ * C) typeclass == pseudo_class_New
+ *
+ * This is an 'uninitialized object' type. elementclass can be
+ * cast to instruction* and points to the NEW instruction
+ * responsible for creating this type.
+ *
+ * Use TYPEINFO_NEWOBJECT_INSTRUCTION to access the pointer in
+ * elementclass.
+ * Don't access other fields of the struct.
+ *
+ * D) typeclass == pseudo_class_Arraystub
+ *
+ * This type is used to represent the result of merging array types
+ * with incompatible component types. An arraystub allows no access
+ * to its components (since their type is undefined), but it allows
+ * operations which act directly on an arbitrary array type (such as
+ * requesting the array size).
+ *
+ * NOTE: An array stub does *not* count as an array. It has dimension
+ * zero.
+ *
+ * Otherwise like a normal class reference type.
+ * Don't access other fields of the struct.
+ *
+ * E) typeclass is an array class
+ *
+ * An array reference.
+ * elementclass...typeclass of the element type
+ * dimension......dimension of the array (>=1)
+ * elementtype....element type (ARRAYTYPE_...)
+ * merged.........mergedlist of the element type
+ *
+ * Use TYPEINFO_IS_ARRAY to check for this case.
+ *
+ * The elementclass may be one of the following:
+ * 1) pseudo_class_Arraystub
+ * 2) an unresolved type
+ * 3) a loaded interface
+ * 4) a loaded (non-pseudo-,non-array-)class != (BOOTSTRAP)java.lang.Object
+ * Note: `merged` may be used
+ * 5) (BOOTSTRAP)java.lang.Object
+ * Note: `merged` may be used
+ *
+ * For the semantics of the merged field in cases 4) and 5) consult the
+ * corresponding descriptions with `elementclass` replaced by `typeclass`.
+ *
+ * F) typeclass is an unresolved type (a symbolic class/interface reference)
+ *
+ * The type has not been resolved yet. (Meaning it corresponds to an
+ * unloaded class or interface).
+ * Don't access other fields of the struct.
+ *
+ * G) typeclass is a loaded interface
+ *
+ * An interface reference type.
+ * Don't access other fields of the struct.
+ *
+ * H) typeclass is a loaded (non-pseudo-,non-array-)class != (BOOTSTRAP)java.lang.Object
+ *
+ * A loaded class type.
+ * All classref_or_classinfos in u.merged.list (if any) are
+ * loaded subclasses of typeclass (no interfaces, array classes, or
+ * unresolved types).
+ * Don't access other fields of the struct.
+ *
+ * I) typeclass is (BOOTSTRAP)java.lang.Object
+ *
+ * The most general kind of reference type.
+ * In this case u.merged.count and u.merged.list
+ * are valid and may be non-zero.
+ * The classref_or_classinfos in u.merged.list (if any) may be
+ * classes, interfaces, pseudo classes or unresolved types.
+ * Don't access other fields of the struct.
+ */
+
+/* The following algorithm is used to determine if the type described
+ * by this typeinfo struct supports the interface X: * XXX add MAYBE *
+ *
+ * 1) If typeclass is X or a subinterface of X the answer is "yes".
+ * 2) If typeclass is a (pseudo) class implementing X the answer is "yes".
+ * 3) If typeclass is not an array and u.merged.count>0
+ * and all classes/interfaces in u.merged.list implement X
+ * the answer is "yes".
+ * 4) If none of the above is true the answer is "no".
+ */
+
+/*
+ * CAUTION: The typeinfo structure should be considered opaque outside of
+ * typeinfo.[ch]. Please use the macros and functions defined here to
+ * access typeinfo structures!
+ */
+struct typeinfo {
+ classref_or_classinfo typeclass;
+ classref_or_classinfo elementclass; /* valid if dimension>0 */ /* various uses! */
+ typeinfo_mergedlist_t *merged;
+ u1 dimension;
+ u1 elementtype; /* valid if dimension>0 */
+};
+
+struct typeinfo_mergedlist {
+ s4 count;
+ classref_or_classinfo list[1]; /* variable length! */
+};
+
+/* a type descriptor stores a basic type and the typeinfo */
+/* this is used for storing the type of a local variable, and for */
+/* storing types in the signature of a method */
+
+struct typedescriptor {
+ typeinfo_t typeinfo; /* valid if type == TYPE_ADR */
+ u1 type; /* basic type (TYPE_INT, ...) */
+};
+
+/****************************************************************************/
+/* MACROS */
+/****************************************************************************/
+
+/* NOTE: The TYPEINFO macros take typeinfo *structs*, not pointers as
+ * arguments. You have to dereference any pointers.
+ */
+
+/* typevectors **************************************************************/
+
+#define TYPEVECTOR_SIZE(size) \
+ ((size) * sizeof(varinfo))
+
+#define DNEW_TYPEVECTOR(size) \
+ ((varinfo *) DumpMemory::allocate(TYPEVECTOR_SIZE(size)))
+
+#define DMNEW_TYPEVECTOR(num,size) \
+ ((varinfo *) DumpMemory::allocate((num) * TYPEVECTOR_SIZE(size)))
+
+#define MGET_TYPEVECTOR(array,index,size) \
+ ((varinfo*) (((u1*)(array)) + TYPEVECTOR_SIZE(size) * (index)))
+
+/* internally used macros ***************************************************/
+
+/* internal, don't use this explicitly! */
+#define TYPEINFO_ALLOCMERGED(mergedlist,count) \
+ do {(mergedlist) = (typeinfo_mergedlist_t *) DumpMemory::allocate(sizeof(typeinfo_mergedlist_t) \
+ + ((count)-1)*sizeof(classinfo*));} while(0)
+
+/* internal, don't use this explicitly! */
+#define TYPEINFO_FREEMERGED(mergedlist)
+
+/* internal, don't use this explicitly! */
+#define TYPEINFO_FREEMERGED_IF_ANY(mergedlist)
+
+/* macros for type queries **************************************************/
+
+#define TYPEINFO_IS_PRIMITIVE(info) \
+ ((info).typeclass.any == NULL)
+
+#define TYPEINFO_IS_REFERENCE(info) \
+ ((info).typeclass.any != NULL)
+
+#define TYPEINFO_IS_NULLTYPE(info) \
+ ((info).typeclass.cls == pseudo_class_Null)
+
+#define TYPEINFO_IS_NEWOBJECT(info) \
+ ((info).typeclass.cls == pseudo_class_New)
+
+#define TYPEINFO_IS_JAVA_LANG_CLASS(info) \
+ ((info).typeclass.cls == class_java_lang_Class)
+
+/* only use this if TYPEINFO_IS_PRIMITIVE returned true! */
+#define TYPEINFO_RETURNADDRESS(info) \
+ ((info).elementclass.any)
+
+/* only use this if TYPEINFO_IS_NEWOBJECT returned true! */
+#define TYPEINFO_NEWOBJECT_INSTRUCTION(info) \
+ ((info).elementclass.any)
+
+/* only use this if TYPEINFO_IS_JAVA_LANG_CLASS returned true! */
+#define TYPEINFO_JAVA_LANG_CLASS_CLASSREF(info) \
+ ((info).elementclass.ref)
+
+/* macros for array type queries ********************************************/
+
+#define TYPEINFO_IS_ARRAY(info) \
+ ( TYPEINFO_IS_REFERENCE(info) \
+ && ((info).dimension != 0) )
+
+#define TYPEINFO_IS_SIMPLE_ARRAY(info) \
+ ( ((info).dimension == 1) )
+
+#define TYPEINFO_IS_ARRAY_ARRAY(info) \
+ ( ((info).dimension >= 2) )
+
+#define TYPEINFO_IS_PRIMITIVE_ARRAY(info,arraytype) \
+ ( TYPEINFO_IS_SIMPLE_ARRAY(info) \
+ && ((info).elementtype == (arraytype)) )
+
+#define TYPEINFO_IS_OBJECT_ARRAY(info) \
+ ( TYPEINFO_IS_SIMPLE_ARRAY(info) \
+ && ((info).elementclass.any != NULL) )
+
+/* assumes that info describes an array type */
+#define TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info) \
+ ( ((info).elementclass.any != NULL) \
+ || ((info).dimension >= 2) )
+
+#define TYPEINFO_IS_ARRAY_OF_REFS(info) \
+ ( TYPEINFO_IS_ARRAY(info) \
+ && TYPEINFO_IS_ARRAY_OF_REFS_NOCHECK(info) )
+
+#define TYPE_IS_RETURNADDRESS(type,info) \
+ ( ((type)==TYPE_RET) \
+ && TYPEINFO_IS_PRIMITIVE(info) )
+
+#define TYPE_IS_REFERENCE(type,info) \
+ ( ((type)==TYPE_ADR) \
+ && !TYPEINFO_IS_PRIMITIVE(info) )
+
+#define TYPEDESC_IS_RETURNADDRESS(td) \
+ TYPE_IS_RETURNADDRESS((td).type,(td).typeinfo)
+
+#define TYPEDESC_IS_REFERENCE(td) \
+ TYPE_IS_REFERENCE((td).type,(td).typeinfo)
+
+/* queries allowing the null type ********************************************/
+
+#define TYPEINFO_MAYBE_ARRAY(info) \
+ (TYPEINFO_IS_ARRAY(info) || TYPEINFO_IS_NULLTYPE(info))
+
+#define TYPEINFO_MAYBE_PRIMITIVE_ARRAY(info,at) \
+ (TYPEINFO_IS_PRIMITIVE_ARRAY(info,at) || TYPEINFO_IS_NULLTYPE(info))
+
+#define TYPEINFO_MAYBE_ARRAY_OF_REFS(info) \
+ (TYPEINFO_IS_ARRAY_OF_REFS(info) || TYPEINFO_IS_NULLTYPE(info))
+
+/* macros for initializing typeinfo structures ******************************/
+
+#define TYPEINFO_INIT_PRIMITIVE(info) \
+ do {(info).typeclass.any = NULL; \
+ (info).elementclass.any = NULL; \
+ (info).merged = NULL; \
+ (info).dimension = 0; \
+ (info).elementtype = 0;} while(0)
+
+#define TYPEINFO_INIT_RETURNADDRESS(info,adr) \
+ do {(info).typeclass.any = NULL; \
+ (info).elementclass.any = (adr); \
+ (info).merged = NULL; \
+ (info).dimension = 0; \
+ (info).elementtype = 0;} while(0)
+
+#define TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,cinfo) \
+ do {(info).typeclass.cls = (cinfo); \
+ (info).elementclass.any = NULL; \
+ (info).merged = NULL; \
+ (info).dimension = 0; \
+ (info).elementtype = 0;} while(0)
+
+#define TYPEINFO_INIT_JAVA_LANG_CLASS(info,c) \
+ do {(info).typeclass.any = class_java_lang_Class; \
+ (info).elementclass = (c); \
+ (info).merged = NULL; \
+ (info).dimension = 0; \
+ (info).elementtype = 0;} while(0)
+
+#define TYPEINFO_INIT_NULLTYPE(info) \
+ TYPEINFO_INIT_NON_ARRAY_CLASSINFO(info,pseudo_class_Null)
+
+#define TYPEINFO_INIT_NEWOBJECT(info,instr) \
+ do {(info).typeclass.cls = pseudo_class_New; \
+ (info).elementclass.any = (instr); \
+ (info).merged = NULL; \
+ (info).dimension = 0; \
+ (info).elementtype = 0;} while(0)
+
+#define TYPEINFO_INIT_PRIMITIVE_ARRAY(info,arraytype) \
+ typeinfo_init_classinfo(&(info),primitivetype_table[arraytype].arrayclass);
+
+/* macros for copying types (destinition is not checked or freed) ***********/
+
+/* TYPEINFO_COPY makes a shallow copy, the merged pointer is simply copied. */
+#define TYPEINFO_COPY(src,dst) \
+ do {(dst) = (src);} while(0)
+
+/* TYPEINFO_CLONE makes a deep copy, the merged list (if any) is duplicated
+ * into a newly allocated array.
+ */
+#define TYPEINFO_CLONE(src,dst) \
+ do {(dst) = (src); \
+ if ((dst).merged) typeinfo_clone(&(src),&(dst));} while(0)
+
+/****************************************************************************/
+/* FUNCTIONS */
+/****************************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* typevector functions *****************************************************/
+
+/* element read-only access */
+bool typevector_checktype(varinfo *set,int index,int type);
+bool typevector_checkreference(varinfo *set,int index);
+bool typevector_checkretaddr(varinfo *set,int index);
+
+/* element write access */
+void typevector_store(varinfo *set,int index,int type,typeinfo_t *info);
+void typevector_store_retaddr(varinfo *set,int index,typeinfo_t *info);
+bool typevector_init_object(varinfo *set,void *ins,classref_or_classinfo initclass,int size);
+
+/* vector functions */
+varinfo *typevector_copy(varinfo *src,int size);
+void typevector_copy_inplace(varinfo *src,varinfo *dst,int size);
+typecheck_result typevector_merge(methodinfo *m,varinfo *dst,varinfo *y,int size);
+
+/* inquiry functions (read-only) ********************************************/
+
+bool typeinfo_is_array(typeinfo_t *info);
+bool typeinfo_is_primitive_array(typeinfo_t *info,int arraytype);
+bool typeinfo_is_array_of_refs(typeinfo_t *info);
+
+typecheck_result typeinfo_is_assignable(typeinfo_t *value,typeinfo_t *dest);
+typecheck_result typeinfo_is_assignable_to_class(typeinfo_t *value,classref_or_classinfo dest);
+
+/* initialization functions *************************************************/
+
+/* RETURN VALUE (bool):
+ * true.............ok,
+ * false............an exception has been thrown.
+ *
+ * RETURN VALUE (int):
+ * >= 0.............ok,
+ * -1...............an exception has been thrown.
+ */
+void typeinfo_init_classinfo(typeinfo_t *info,classinfo *c);
+bool typeinfo_init_class(typeinfo_t *info,classref_or_classinfo c);
+bool typeinfo_init_component(typeinfo_t *srcarray,typeinfo_t *dst);
+
+bool typeinfo_init_from_typedesc(typedesc *desc,u1 *type,typeinfo_t *info);
+bool typeinfos_init_from_methoddesc(methoddesc *desc,u1 *typebuf,
+ typeinfo_t *infobuf,
+ int buflen,bool twoword,
+ u1 *returntype,typeinfo_t *returntypeinfo);
+bool typedescriptor_init_from_typedesc(typedescriptor_t *td,
+ typedesc *desc);
+bool typeinfo_init_varinfo_from_typedesc(varinfo *var,
+ typedesc *desc);
+int typedescriptors_init_from_methoddesc(typedescriptor_t *td,
+ methoddesc *desc,
+ int buflen,bool twoword,int startindex,
+ typedescriptor_t *returntype);
+bool typeinfo_init_varinfos_from_methoddesc(varinfo *vars,
+ methoddesc *desc,
+ int buflen, int startindex,
+ s4 *map,
+ typedescriptor_t *returntype);
+
+void typeinfo_clone(typeinfo_t *src,typeinfo_t *dest);
+
+/* freeing memory ***********************************************************/
+
+void typeinfo_free(typeinfo_t *info);
+
+/* functions for merging types **********************************************/
+
+typecheck_result typeinfo_merge(methodinfo *m,typeinfo_t *dest,typeinfo_t* y);
+
+/* debugging helpers ********************************************************/
+
+#ifdef TYPEINFO_DEBUG
+
+#include <stdio.h>
+
+void typeinfo_test();
+void typeinfo_print_class(FILE *file,classref_or_classinfo c);
+void typeinfo_print(FILE *file,typeinfo_t *info,int indent);
+void typeinfo_print_short(FILE *file,typeinfo_t *info);
+void typeinfo_print_type(FILE *file,int type,typeinfo_t *info);
+void typedescriptor_print(FILE *file,typedescriptor_t *td);
+void typevector_print(FILE *file,varinfo *vec,int size);
+
+#endif /* TYPEINFO_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TYPEINFO_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
#include "vm/jit/jit.hpp"
#include "vm/jit/linenumbertable.hpp"
#include "vm/jit/methodheader.h"
-#include "vm/jit/parse.h"
+#include "vm/jit/parse.hpp"
#include "vm/jit/patcher-common.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/replace.hpp"
#include "vm/jit/x86_64/md-abi.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/global.h"
#include "vm/jit/abi.h"
+++ /dev/null
-/* src/vm/linker.c - class linker functions
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <stdint.h>
-
-#include "vm/types.h"
-
-#include "mm/memory.h"
-
-#include "native/native.hpp"
-
-#include "threads/lock.hpp"
-#include "threads/mutex.hpp"
-
-#include "toolbox/logging.h"
-
-#include "vm/access.h"
-#include "vm/array.hpp"
-#include "vm/class.hpp"
-#include "vm/classcache.h"
-#include "vm/exceptions.hpp"
-#include "vm/globals.hpp"
-#include "vm/loader.hpp"
-#include "vm/options.h"
-#include "vm/primitive.hpp"
-#include "vm/rt-timing.h"
-#include "vm/string.hpp"
-#include "vm/vm.hpp"
-
-#include "vm/jit/asmpart.h"
-#include "vm/jit/stubs.hpp"
-
-
-/* debugging macros ***********************************************************/
-
-#if !defined(NDEBUG)
-# define TRACELINKCLASS(c) \
- do { \
- if (opt_TraceLinkClass) { \
- log_start(); \
- log_print("[Linking "); \
- class_print((c)); \
- log_print("]"); \
- log_finish(); \
- } \
- } while (0)
-#else
-# define TRACELINKCLASS(c)
-#endif
-
-
-/* #include "vm/resolve.hpp" */
-/* copied prototype to avoid bootstrapping problem: */
-classinfo *resolve_classref_or_classinfo_eager(classref_or_classinfo cls, bool checkaccess);
-
-#if defined(ENABLE_STATISTICS)
-# include "vm/statistics.h"
-#endif
-
-#if !defined(NDEBUG) && defined(ENABLE_INLINING)
-#define INLINELOG(code) do { if (opt_TraceInlining) { code } } while (0)
-#else
-#define INLINELOG(code)
-#endif
-
-
-/* global variables ***********************************************************/
-
-static s4 interfaceindex; /* sequential numbering of interfaces */
-static s4 classvalue;
-
-Mutex *linker_classrenumber_mutex;
-
-
-/* private functions **********************************************************/
-
-static classinfo *link_class_intern(classinfo *c);
-static arraydescriptor *link_array(classinfo *c);
-static void linker_compute_class_values(classinfo *c);
-static void linker_compute_subclasses(classinfo *c);
-static bool linker_addinterface(classinfo *c, classinfo *ic);
-static s4 class_highestinterface(classinfo *c);
-
-
-/* linker_init *****************************************************************
-
- Initializes the linker subsystem and links classes required for the
- primitive table.
-
-*******************************************************************************/
-
-void linker_preinit(void)
-{
- TRACESUBSYSTEMINITIALIZATION("linker_preinit");
-
- /* Reset interface index. */
-
- interfaceindex = 0;
-
-#if defined(ENABLE_THREADS)
- /* create the global mutex */
-
- linker_classrenumber_mutex = Mutex_new();
-#endif
-
- /* Link the most basic classes. */
-
- if (!link_class(class_java_lang_Object))
- vm_abort("linker_preinit: linking java/lang/Object failed");
-
-#if defined(ENABLE_JAVASE)
- if (!link_class(class_java_lang_Cloneable))
- vm_abort("linker_preinit: linking java/lang/Cloneable failed");
-
- if (!link_class(class_java_io_Serializable))
- vm_abort("linker_preinit: linking java/io/Serializable failed");
-#endif
-}
-
-
-/* linker_init *****************************************************************
-
- Links all classes required in the VM.
-
-*******************************************************************************/
-
-void linker_init(void)
-{
- TRACESUBSYSTEMINITIALIZATION("linker_init");
-
- /* Link java.lang.Class as first class of the system, because we
- need it's vftbl for all other classes so we can use a class as
- object. */
-
- if (!link_class(class_java_lang_Class))
- vm_abort("linker_init: linking java/lang/Class failed");
-
- /* Now set the header.vftbl of all classes which were created
- before java.lang.Class was linked. */
-
- class_postset_header_vftbl();
-
- /* Link primitive-type wrapping classes. */
-
-#if defined(ENABLE_JAVASE)
- if (!link_class(class_java_lang_Void))
- vm_abort("linker_init: linking failed");
-#endif
-
- if (!link_class(class_java_lang_Boolean))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Byte))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Character))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Short))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Integer))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Long))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Float))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Double))
- vm_abort("linker_init: linking failed");
-
- /* Link important system classes. */
-
- if (!link_class(class_java_lang_String))
- vm_abort("linker_init: linking java/lang/String failed");
-
-#if defined(ENABLE_JAVASE)
- if (!link_class(class_java_lang_ClassLoader))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_SecurityManager))
- vm_abort("linker_init: linking failed");
-#endif
-
- if (!link_class(class_java_lang_System))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_Thread))
- vm_abort("linker_init: linking failed");
-
-#if defined(ENABLE_JAVASE)
- if (!link_class(class_java_lang_ThreadGroup))
- vm_abort("linker_init: linking failed");
-#endif
-
- if (!link_class(class_java_lang_Throwable))
- vm_abort("linker_init: linking failed");
-
-#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
- if (!link_class(class_java_lang_VMSystem))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_VMThread))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_VMThrowable))
- vm_abort("linker_init: linking failed");
-#endif
-
- /* Important system exceptions. */
-
- if (!link_class(class_java_lang_Exception))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_ClassNotFoundException))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_RuntimeException))
- vm_abort("linker_init: linking failed");
-
- /* some classes which may be used more often */
-
-#if defined(ENABLE_JAVASE)
- if (!link_class(class_java_lang_StackTraceElement))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_reflect_Constructor))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_reflect_Field))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_reflect_Method))
- vm_abort("linker_init: linking failed");
-
-# if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
- if (!link_class(class_java_lang_reflect_VMConstructor))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_reflect_VMField))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_lang_reflect_VMMethod))
- vm_abort("linker_init: linking failed");
-# endif
-
- if (!link_class(class_java_security_PrivilegedAction))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_util_Vector))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_java_util_HashMap))
- vm_abort("linker_init: linking failed");
-
-# if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
- if (!link_class(class_sun_misc_Signal))
- vm_abort("linker_init: linking failed");
-
- if (!link_class(class_sun_reflect_MagicAccessorImpl))
- vm_abort("linker_init: linking failed");
-# endif
-
- if (!link_class(arrayclass_java_lang_Object))
- vm_abort("linker_init: linking failed");
-#endif
-
-
- /* create pseudo classes used by the typechecker */
-
- /* pseudo class for Arraystubs (extends java.lang.Object) */
-
- pseudo_class_Arraystub =
- class_create_classinfo(utf_new_char("$ARRAYSTUB$"));
- pseudo_class_Arraystub->state |= CLASS_LOADED;
- pseudo_class_Arraystub->super = class_java_lang_Object;
-
-#if defined(ENABLE_JAVASE)
-
- pseudo_class_Arraystub->interfacescount = 2;
- pseudo_class_Arraystub->interfaces = MNEW(classinfo*, 2);
- pseudo_class_Arraystub->interfaces[0] = class_java_lang_Cloneable;
- pseudo_class_Arraystub->interfaces[1] = class_java_io_Serializable;
-
-#elif defined(ENABLE_JAVAME_CLDC1_1)
-
- pseudo_class_Arraystub->interfacescount = 0;
- pseudo_class_Arraystub->interfaces = NULL;
-
-#else
-# error unknown Java configuration
-#endif
-
- if (!classcache_store_unique(pseudo_class_Arraystub))
- vm_abort("linker_init: could not cache pseudo_class_Arraystub");
-
- if (!link_class(pseudo_class_Arraystub))
- vm_abort("linker_init: linking pseudo_class_Arraystub failed");
-
- /* pseudo class representing the null type */
-
- pseudo_class_Null = class_create_classinfo(utf_new_char("$NULL$"));
- pseudo_class_Null->state |= CLASS_LOADED;
- pseudo_class_Null->super = class_java_lang_Object;
-
- if (!classcache_store_unique(pseudo_class_Null))
- vm_abort("linker_init: could not cache pseudo_class_Null");
-
- if (!link_class(pseudo_class_Null))
- vm_abort("linker_init: linking failed");
-
- /* pseudo class representing new uninitialized objects */
-
- pseudo_class_New = class_create_classinfo(utf_new_char("$NEW$"));
- pseudo_class_New->state |= CLASS_LOADED;
- pseudo_class_New->state |= CLASS_LINKED; /* XXX is this allright? */
- pseudo_class_New->super = class_java_lang_Object;
-
- if (!classcache_store_unique(pseudo_class_New))
- vm_abort("linker_init: could not cache pseudo_class_New");
-
- /* Correct vftbl-entries (retarded loading and linking of class
- java/lang/String). */
-
- stringtable_update();
-}
-
-
-/* link_class ******************************************************************
-
- Wrapper function for link_class_intern to ease monitor enter/exit
- and exception handling.
-
-*******************************************************************************/
-
-classinfo *link_class(classinfo *c)
-{
- classinfo *r;
-#if defined(ENABLE_RT_TIMING)
- struct timespec time_start, time_end;
-#endif
-
- RT_TIMING_GET_TIME(time_start);
-
- if (c == NULL) {
- exceptions_throw_nullpointerexception();
- return NULL;
- }
-
- LOCK_MONITOR_ENTER(c);
-
- /* Maybe the class is currently linking or is already linked.*/
-
- if ((c->state & CLASS_LINKING) || (c->state & CLASS_LINKED)) {
- LOCK_MONITOR_EXIT(c);
-
- return c;
- }
-
-#if defined(ENABLE_STATISTICS)
- /* measure time */
-
- if (opt_getcompilingtime)
- compilingtime_stop();
-
- if (opt_getloadingtime)
- loadingtime_start();
-#endif
-
- /* call the internal function */
-
- r = link_class_intern(c);
-
- /* If return value is NULL, we had a problem and the class is not
- linked. */
-
- if (r == NULL)
- c->state &= ~CLASS_LINKING;
-
-#if defined(ENABLE_STATISTICS)
- /* measure time */
-
- if (opt_getloadingtime)
- loadingtime_stop();
-
- if (opt_getcompilingtime)
- compilingtime_start();
-#endif
-
- LOCK_MONITOR_EXIT(c);
-
- RT_TIMING_GET_TIME(time_end);
-
- RT_TIMING_TIME_DIFF(time_start,time_end,RT_TIMING_LINK_TOTAL);
-
- return r;
-}
-
-
-/* linker_overwrite_method *****************************************************
-
- Overwrite a method with another one, update method flags and check
- assumptions.
-
- IN:
- mg................the general method being overwritten
- ms................the overwriting (more specialized) method
- wl................worklist where to add invalidated methods
-
- RETURN VALUE:
- true..............everything ok
- false.............an exception has been thrown
-
-*******************************************************************************/
-
-static bool linker_overwrite_method(methodinfo *mg,
- methodinfo *ms,
- method_worklist **wl)
-{
- classinfo *cg;
- classinfo *cs;
-
- cg = mg->clazz;
- cs = ms->clazz;
-
- /* overriding a final method is illegal */
-
- if (mg->flags & ACC_FINAL) {
- exceptions_throw_verifyerror(mg, "Overriding final method");
- return false;
- }
-
- /* method ms overwrites method mg */
-
-#if defined(ENABLE_VERIFIER)
- /* Add loading constraints (for the more general types of method mg). */
- /* Not for <init>, as it is not invoked virtually. */
-
- if ((ms->name != utf_init)
- && !classcache_add_constraints_for_params(
- cs->classloader, cg->classloader, mg))
- {
- return false;
- }
-#endif
-
- /* inherit the vftbl index, and record the overwriting */
-
- ms->vftblindex = mg->vftblindex;
- ms->overwrites = mg;
-
- /* update flags and check assumptions */
- /* <init> methods are a special case, as they are never dispatched dynamically */
-
- if ((ms->flags & ACC_METHOD_IMPLEMENTED) && ms->name != utf_init) {
- do {
-
-#if defined(ENABLE_TLH)
- if (mg->flags & ACC_METHOD_MONOMORPHY_USED) {
- printf("%s/%s is evil! the siner is %s/%s\n", mg->clazz->name->text, mg->name->text,
- ms->clazz->name->text, ms->name->text);
- ms->flags |= ACC_METHOD_PARENT_MONOMORPHY_USED;
- }
-#endif
-
- if (mg->flags & ACC_METHOD_IMPLEMENTED) {
- /* this adds another implementation */
-
- mg->flags &= ~ACC_METHOD_MONOMORPHIC;
-
- INLINELOG( printf("becomes polymorphic: "); method_println(mg); );
-
- method_break_assumption_monomorphic(mg, wl);
- }
- else {
- /* this is the first implementation */
-
- mg->flags |= ACC_METHOD_IMPLEMENTED;
-
- INLINELOG( printf("becomes implemented: "); method_println(mg); );
- }
-
- ms = mg;
- mg = mg->overwrites;
- } while (mg != NULL);
- }
-
- return true;
-}
-
-
-/* link_class_intern ***********************************************************
-
- Tries to link a class. The function calculates the length in bytes
- that an instance of this class requires as well as the VTBL for
- methods and interface methods.
-
-*******************************************************************************/
-
-static classinfo *link_class_intern(classinfo *c)
-{
- classinfo *super; /* super class */
- classinfo *tc; /* temporary class variable */
- s4 supervftbllength; /* vftbllegnth of super class */
- s4 vftbllength; /* vftbllength of current class */
- s4 interfacetablelength; /* interface table length */
- vftbl_t *v; /* vftbl of current class */
- s4 i; /* interface/method/field counter */
- arraydescriptor *arraydesc; /* descriptor for array classes */
- method_worklist *worklist; /* worklist for recompilation */
-#if defined(ENABLE_RT_TIMING)
- struct timespec time_start, time_resolving, time_compute_vftbl,
- time_abstract, time_compute_iftbl, time_fill_vftbl,
- time_offsets, time_fill_iftbl, time_finalizer,
- time_subclasses;
-#endif
-
- RT_TIMING_GET_TIME(time_start);
-
- TRACELINKCLASS(c);
-
- /* the class must be loaded */
-
- /* XXX should this be a specific exception? */
- assert(c->state & CLASS_LOADED);
-
- /* This is check in link_class. */
-
- assert(!(c->state & CLASS_LINKED));
-
- /* cache the self-reference of this class */
- /* we do this for cases where the defining loader of the class */
- /* has not yet been recorded as an initiating loader for the class */
- /* this is needed so subsequent code can assume that self-refs */
- /* will always resolve lazily */
- /* No need to do it for the bootloader - it is always registered */
- /* as initiating loader for the classes it loads. */
- if (c->classloader)
- classcache_store(c->classloader,c,false);
-
- /* this class is currently linking */
-
- c->state |= CLASS_LINKING;
-
- arraydesc = NULL;
- worklist = NULL;
-
- /* Link the super interfaces. */
-
- for (i = 0; i < c->interfacescount; i++) {
- tc = c->interfaces[i];
-
- if (!(tc->state & CLASS_LINKED))
- if (!link_class(tc))
- return NULL;
- }
-
- /* check super class */
-
- super = NULL;
-
- /* Check for java/lang/Object. */
-
- if (c->super == NULL) {
- c->index = 0;
- c->instancesize = sizeof(java_object_t);
-
- vftbllength = supervftbllength = 0;
-
- c->finalizer = NULL;
- }
- else {
- /* Get super class. */
-
- super = c->super;
-
- /* Link the super class if necessary. */
-
- if (!(super->state & CLASS_LINKED))
- if (!link_class(super))
- return NULL;
-
- /* OR the ACC_CLASS_HAS_POINTERS and the ACC_CLASS_REFERENCE_*
- flags. */
-
- c->flags |= (super->flags &
- (ACC_CLASS_HAS_POINTERS | ACC_CLASS_REFERENCE_MASK));
-
- /* handle array classes */
-
- if (c->name->text[0] == '[')
- if (!(arraydesc = link_array(c)))
- return NULL;
-
- if (c->flags & ACC_INTERFACE)
- c->index = interfaceindex++;
- else
- c->index = super->index + 1;
-
- c->instancesize = super->instancesize;
-
- vftbllength = supervftbllength = super->vftbl->vftbllength;
-
- c->finalizer = super->finalizer;
- }
- RT_TIMING_GET_TIME(time_resolving);
-
-
- /* compute vftbl length */
-
- for (i = 0; i < c->methodscount; i++) {
- methodinfo *m = &(c->methods[i]);
-
- if (!(m->flags & ACC_STATIC)) { /* is instance method */
- tc = super;
-
- while (tc) {
- s4 j;
-
- for (j = 0; j < tc->methodscount; j++) {
- if (method_canoverwrite(m, &(tc->methods[j]))) {
- if (tc->methods[j].flags & ACC_PRIVATE)
- goto notfoundvftblindex;
-
- /* package-private methods in other packages */
- /* must not be overridden */
- /* (see Java Language Specification 8.4.8.1) */
- if ( !(tc->methods[j].flags & (ACC_PUBLIC | ACC_PROTECTED))
- && !SAME_PACKAGE(c,tc) )
- {
- goto notfoundvftblindex;
- }
-
- if (!linker_overwrite_method(&(tc->methods[j]), m, &worklist))
- return NULL;
-
- goto foundvftblindex;
- }
- }
-
- tc = tc->super;
- }
-
- notfoundvftblindex:
- m->vftblindex = (vftbllength++);
- foundvftblindex:
- ;
- }
- }
- RT_TIMING_GET_TIME(time_compute_vftbl);
-
-
- /* Check all interfaces of an abstract class (maybe be an
- interface too) for unimplemented methods. Such methods are
- called miranda-methods and are marked with the ACC_MIRANDA
- flag. VMClass.getDeclaredMethods does not return such
- methods. */
-
- if (c->flags & ACC_ABSTRACT) {
- classinfo *ic;
- methodinfo *im;
- s4 abstractmethodscount;
- s4 j;
- s4 k;
-
- abstractmethodscount = 0;
-
- /* check all interfaces of the abstract class */
-
- for (i = 0; i < c->interfacescount; i++) {
- ic = c->interfaces[i];
-
- for (j = 0; j < ic->methodscount; j++) {
- im = &(ic->methods[j]);
-
- /* skip `<clinit>' and `<init>' */
-
- if ((im->name == utf_clinit) || (im->name == utf_init))
- continue;
-
- for (tc = c; tc != NULL; tc = tc->super) {
- for (k = 0; k < tc->methodscount; k++) {
- if (method_canoverwrite(im, &(tc->methods[k])))
- goto noabstractmethod;
- }
- }
-
- abstractmethodscount++;
-
- noabstractmethod:
- ;
- }
- }
-
- if (abstractmethodscount > 0) {
- methodinfo *am;
-
- /* reallocate methods memory */
-
- c->methods = MREALLOC(c->methods, methodinfo, c->methodscount,
- c->methodscount + abstractmethodscount);
-
- for (i = 0; i < c->interfacescount; i++) {
- ic = c->interfaces[i];
-
- for (j = 0; j < ic->methodscount; j++) {
- im = &(ic->methods[j]);
-
- /* skip `<clinit>' and `<init>' */
-
- if ((im->name == utf_clinit) || (im->name == utf_init))
- continue;
-
- for (tc = c; tc != NULL; tc = tc->super) {
- for (k = 0; k < tc->methodscount; k++) {
- if (method_canoverwrite(im, &(tc->methods[k])))
- goto noabstractmethod2;
- }
- }
-
- /* Copy the method found into the new c->methods
- array and tag it as miranda-method. */
-
- am = &(c->methods[c->methodscount]);
- c->methodscount++;
-
- MCOPY(am, im, methodinfo, 1);
-
- am->vftblindex = (vftbllength++);
- am->clazz = c;
- am->flags |= ACC_MIRANDA;
-
- noabstractmethod2:
- ;
- }
- }
- }
- }
- RT_TIMING_GET_TIME(time_abstract);
-
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat)
- count_vftbl_len +=
- sizeof(vftbl_t) + (sizeof(methodptr) * (vftbllength - 1));
-#endif
-
- /* compute interfacetable length */
-
- interfacetablelength = 0;
-
- for (tc = c; tc != NULL; tc = tc->super) {
- for (i = 0; i < tc->interfacescount; i++) {
- s4 h = class_highestinterface(tc->interfaces[i]) + 1;
-
- if (h > interfacetablelength)
- interfacetablelength = h;
- }
- }
- RT_TIMING_GET_TIME(time_compute_iftbl);
-
- /* allocate virtual function table */
-
- v = (vftbl_t *) mem_alloc(sizeof(vftbl_t) +
- sizeof(methodptr) * (vftbllength - 1) +
- sizeof(methodptr*) * (interfacetablelength - (interfacetablelength > 0)));
- v = (vftbl_t *) (((methodptr *) v) +
- (interfacetablelength - 1) * (interfacetablelength > 1));
-
- c->vftbl = v;
- v->clazz = c;
- v->vftbllength = vftbllength;
- v->interfacetablelength = interfacetablelength;
- v->arraydesc = arraydesc;
-
- /* store interface index in vftbl */
-
- if (c->flags & ACC_INTERFACE)
- v->baseval = -(c->index);
-
- /* copy virtual function table of super class */
-
- for (i = 0; i < supervftbllength; i++)
- v->table[i] = super->vftbl->table[i];
-
- /* Fill the remaining vftbl slots with the AbstractMethodError
- stub (all after the super class slots, because they are already
- initialized). */
-
- for (; i < vftbllength; i++) {
-#if defined(ENABLE_JIT)
-# if defined(ENABLE_INTRP)
- if (opt_intrp)
- v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
- else
-# endif
- v->table[i] = (methodptr) (ptrint) &asm_abstractmethoderror;
-#else
- v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
-#endif
- }
-
- /* add method stubs into virtual function table */
-
- for (i = 0; i < c->methodscount; i++) {
- methodinfo *m = &(c->methods[i]);
-
- assert(m->stubroutine == NULL);
-
- /* Don't create a compiler stub for abstract methods as they
- throw an AbstractMethodError with the default stub in the
- vftbl. This entry is simply copied by sub-classes. */
-
- if (m->flags & ACC_ABSTRACT)
- continue;
-
-#if defined(ENABLE_JIT)
-# if defined(ENABLE_INTRP)
- if (opt_intrp)
- m->stubroutine = intrp_createcompilerstub(m);
- else
-#endif
- m->stubroutine = CompilerStub_generate(m);
-#else
- m->stubroutine = intrp_createcompilerstub(m);
-#endif
-
- /* static methods are not in the vftbl */
-
- if (m->flags & ACC_STATIC)
- continue;
-
- /* insert the stubroutine into the vftbl */
-
- v->table[m->vftblindex] = (methodptr) (ptrint) m->stubroutine;
- }
- RT_TIMING_GET_TIME(time_fill_vftbl);
-
- /* compute instance size and offset of each field */
-
- for (i = 0; i < c->fieldscount; i++) {
- s4 dsize;
- fieldinfo *f = &(c->fields[i]);
-
- if (!(f->flags & ACC_STATIC)) {
- dsize = descriptor_typesize(f->parseddesc);
- c->instancesize = MEMORY_ALIGN(c->instancesize, dsize);
- f->offset = c->instancesize;
- c->instancesize += dsize;
- }
- }
- RT_TIMING_GET_TIME(time_offsets);
-
- /* initialize interfacetable and interfacevftbllength */
-
- v->interfacevftbllength = MNEW(s4, interfacetablelength);
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat)
- count_vftbl_len += (4 + sizeof(s4)) * v->interfacetablelength;
-#endif
-
- for (i = 0; i < interfacetablelength; i++) {
- v->interfacevftbllength[i] = 0;
- v->interfacetable[-i] = NULL;
- }
-
- /* add interfaces */
-
- for (tc = c; tc != NULL; tc = tc->super)
- for (i = 0; i < tc->interfacescount; i++)
- if (!linker_addinterface(c, tc->interfaces[i]))
- return NULL;
-
- RT_TIMING_GET_TIME(time_fill_iftbl);
-
- /* add finalizer method (not for java.lang.Object) */
-
- if (super) {
- methodinfo *fi;
-
- fi = class_findmethod(c, utf_finalize, utf_void__void);
-
- if (fi)
- if (!(fi->flags & ACC_STATIC))
- c->finalizer = fi;
- }
- RT_TIMING_GET_TIME(time_finalizer);
-
- /* final tasks */
-
- linker_compute_subclasses(c);
-
- RT_TIMING_GET_TIME(time_subclasses);
-
- /* revert the linking state and class is linked */
-
- c->state = (c->state & ~CLASS_LINKING) | CLASS_LINKED;
-
- /* check worklist */
-
- /* XXX must this also be done in case of exception? */
-
- while (worklist != NULL) {
- method_worklist *wi = worklist;
-
- worklist = worklist->next;
-
- INLINELOG( printf("MUST BE RECOMPILED: "); method_println(wi->m); );
- jit_invalidate_code(wi->m);
-
- /* XXX put worklist into dump memory? */
- FREE(wi, method_worklist);
- }
-
- RT_TIMING_TIME_DIFF(time_start ,time_resolving ,RT_TIMING_LINK_RESOLVE);
- RT_TIMING_TIME_DIFF(time_resolving ,time_compute_vftbl,RT_TIMING_LINK_C_VFTBL);
- RT_TIMING_TIME_DIFF(time_compute_vftbl,time_abstract ,RT_TIMING_LINK_ABSTRACT);
- RT_TIMING_TIME_DIFF(time_abstract ,time_compute_iftbl,RT_TIMING_LINK_C_IFTBL);
- RT_TIMING_TIME_DIFF(time_compute_iftbl,time_fill_vftbl ,RT_TIMING_LINK_F_VFTBL);
- RT_TIMING_TIME_DIFF(time_fill_vftbl ,time_offsets ,RT_TIMING_LINK_OFFSETS);
- RT_TIMING_TIME_DIFF(time_offsets ,time_fill_iftbl ,RT_TIMING_LINK_F_IFTBL);
- RT_TIMING_TIME_DIFF(time_fill_iftbl ,time_finalizer ,RT_TIMING_LINK_FINALIZER);
- RT_TIMING_TIME_DIFF(time_finalizer ,time_subclasses ,RT_TIMING_LINK_SUBCLASS);
-
- /* just return c to show that we didn't had a problem */
-
- return c;
-}
-
-
-/* link_array ******************************************************************
-
- This function is called by link_class to create the arraydescriptor
- for an array class.
-
- This function returns NULL if the array cannot be linked because
- the component type has not been linked yet.
-
-*******************************************************************************/
-
-static arraydescriptor *link_array(classinfo *c)
-{
- classinfo *comp;
- s4 namelen;
- arraydescriptor *desc;
- vftbl_t *compvftbl;
- utf *u;
-
- comp = NULL;
- namelen = c->name->blength;
-
- /* Check the component type */
-
- switch (c->name->text[1]) {
- case '[':
- /* c is an array of arrays. */
- u = utf_new(c->name->text + 1, namelen - 1);
- if (!(comp = load_class_from_classloader(u, c->classloader)))
- return NULL;
- break;
-
- case 'L':
- /* c is an array of objects. */
- u = utf_new(c->name->text + 2, namelen - 3);
- if (!(comp = load_class_from_classloader(u, c->classloader)))
- return NULL;
- break;
- }
-
- /* If the component type has not been linked, link it now */
-
- assert(!comp || (comp->state & CLASS_LOADED));
-
- if (comp && !(comp->state & CLASS_LINKED))
- if (!link_class(comp))
- return NULL;
-
- /* Allocate the arraydescriptor */
-
- desc = NEW(arraydescriptor);
-
- if (comp) {
- /* c is an array of references */
- desc->arraytype = ARRAYTYPE_OBJECT;
- desc->componentsize = sizeof(void*);
- desc->dataoffset = OFFSET(java_objectarray_t, data);
-
- compvftbl = comp->vftbl;
-
- if (!compvftbl) {
- log_text("Component class has no vftbl");
- assert(0);
- }
-
- desc->componentvftbl = compvftbl;
-
- if (compvftbl->arraydesc) {
- desc->elementvftbl = compvftbl->arraydesc->elementvftbl;
-
- if (compvftbl->arraydesc->dimension >= 255) {
- log_text("Creating array of dimension >255");
- assert(0);
- }
-
- desc->dimension = compvftbl->arraydesc->dimension + 1;
- desc->elementtype = compvftbl->arraydesc->elementtype;
-
- } else {
- desc->elementvftbl = compvftbl;
- desc->dimension = 1;
- desc->elementtype = ARRAYTYPE_OBJECT;
- }
-
- } else {
- /* c is an array of a primitive type */
- switch (c->name->text[1]) {
- case 'Z':
- desc->arraytype = ARRAYTYPE_BOOLEAN;
- desc->dataoffset = OFFSET(java_booleanarray_t,data);
- desc->componentsize = sizeof(u1);
- break;
-
- case 'B':
- desc->arraytype = ARRAYTYPE_BYTE;
- desc->dataoffset = OFFSET(java_bytearray_t,data);
- desc->componentsize = sizeof(u1);
- break;
-
- case 'C':
- desc->arraytype = ARRAYTYPE_CHAR;
- desc->dataoffset = OFFSET(java_chararray_t,data);
- desc->componentsize = sizeof(u2);
- break;
-
- case 'D':
- desc->arraytype = ARRAYTYPE_DOUBLE;
- desc->dataoffset = OFFSET(java_doublearray_t,data);
- desc->componentsize = sizeof(double);
- break;
-
- case 'F':
- desc->arraytype = ARRAYTYPE_FLOAT;
- desc->dataoffset = OFFSET(java_floatarray_t,data);
- desc->componentsize = sizeof(float);
- break;
-
- case 'I':
- desc->arraytype = ARRAYTYPE_INT;
- desc->dataoffset = OFFSET(java_intarray_t,data);
- desc->componentsize = sizeof(s4);
- break;
-
- case 'J':
- desc->arraytype = ARRAYTYPE_LONG;
- desc->dataoffset = OFFSET(java_longarray_t,data);
- desc->componentsize = sizeof(s8);
- break;
-
- case 'S':
- desc->arraytype = ARRAYTYPE_SHORT;
- desc->dataoffset = OFFSET(java_shortarray_t,data);
- desc->componentsize = sizeof(s2);
- break;
-
- default:
- exceptions_throw_noclassdeffounderror(c->name);
- return NULL;
- }
-
- desc->componentvftbl = NULL;
- desc->elementvftbl = NULL;
- desc->dimension = 1;
- desc->elementtype = desc->arraytype;
- }
-
- return desc;
-}
-
-
-/* linker_compute_subclasses ***************************************************
-
- XXX
-
- ATTENTION: DO NOT REMOVE ANY OF THE LOCKING MECHANISMS BELOW:
- This function needs to take the class renumber lock and stop the
- world during class renumbering. The lock is used in C code which
- is not that performance critical. Whereas JIT code uses critical
- sections to atomically access the class values.
-
-*******************************************************************************/
-
-static void linker_compute_subclasses(classinfo *c)
-{
- Mutex_lock(linker_classrenumber_mutex);
-
- if (!(c->flags & ACC_INTERFACE)) {
- c->nextsub = NULL;
- c->sub = NULL;
- }
-
- if (!(c->flags & ACC_INTERFACE) && (c->super != NULL)) {
- c->nextsub = c->super->sub;
- c->super->sub = c;
- }
-
- classvalue = 0;
-
- /* compute class values */
-
- linker_compute_class_values(class_java_lang_Object);
-
- Mutex_unlock(linker_classrenumber_mutex);
-}
-
-
-/* linker_compute_class_values *************************************************
-
- XXX
-
-*******************************************************************************/
-
-static void linker_compute_class_values(classinfo *c)
-{
- classinfo *subs;
-
- c->vftbl->baseval = ++classvalue;
-
- subs = c->sub;
-
- while (subs) {
- linker_compute_class_values(subs);
-
- subs = subs->nextsub;
- }
-
- c->vftbl->diffval = classvalue - c->vftbl->baseval;
-}
-
-
-/* linker_addinterface *********************************************************
-
- Is needed by link_class for adding a VTBL to a class. All
- interfaces implemented by ic are added as well.
-
- RETURN VALUE:
- true.........everything ok
- false........an exception has been thrown
-
-*******************************************************************************/
-
-static bool linker_addinterface(classinfo *c, classinfo *ic)
-{
- s4 j, k;
- vftbl_t *v;
- s4 i;
- classinfo *sc;
- methodinfo *m;
-
- v = c->vftbl;
- i = ic->index;
-
- if (i >= v->interfacetablelength)
- vm_abort("Internal error: interfacetable overflow");
-
- /* if this interface has already been added, return immediately */
-
- if (v->interfacetable[-i] != NULL)
- return true;
-
- if (ic->methodscount == 0) { /* fake entry needed for subtype test */
- v->interfacevftbllength[i] = 1;
- v->interfacetable[-i] = MNEW(methodptr, 1);
- v->interfacetable[-i][0] = NULL;
- }
- else {
- v->interfacevftbllength[i] = ic->methodscount;
- v->interfacetable[-i] = MNEW(methodptr, ic->methodscount);
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat)
- count_vftbl_len += sizeof(methodptr) *
- (ic->methodscount + (ic->methodscount == 0));
-#endif
-
- for (j = 0; j < ic->methodscount; j++) {
- for (sc = c; sc != NULL; sc = sc->super) {
- for (k = 0; k < sc->methodscount; k++) {
- m = &(sc->methods[k]);
-
- if (method_canoverwrite(m, &(ic->methods[j]))) {
- /* method m overwrites the (abstract) method */
-#if defined(ENABLE_VERIFIER)
- /* Add loading constraints (for the more
- general types of the method
- ic->methods[j]). */
- if (!classcache_add_constraints_for_params(
- c->classloader, ic->classloader,
- &(ic->methods[j])))
- {
- return false;
- }
-#endif
-
- /* XXX taken from gcj */
- /* check for ACC_STATIC: IncompatibleClassChangeError */
-
- /* check for !ACC_PUBLIC: IllegalAccessError */
-
- /* check for ACC_ABSTRACT: AbstracMethodError,
- not sure about that one */
-
- v->interfacetable[-i][j] = v->table[m->vftblindex];
- goto foundmethod;
- }
- }
- }
-
- /* If no method was found, insert the AbstractMethodError
- stub. */
-
-#if defined(ENABLE_JIT)
-# if defined(ENABLE_INTRP)
- if (opt_intrp)
- v->interfacetable[-i][j] =
- (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
- else
-# endif
- v->interfacetable[-i][j] =
- (methodptr) (ptrint) &asm_abstractmethoderror;
-#else
- v->interfacetable[-i][j] =
- (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
-#endif
-
- foundmethod:
- ;
- }
- }
-
- /* add superinterfaces of this interface */
-
- for (j = 0; j < ic->interfacescount; j++)
- if (!linker_addinterface(c, ic->interfaces[j]))
- return false;
-
- /* everything ok */
-
- return true;
-}
-
-
-/* class_highestinterface ******************************************************
-
- Used by the function link_class to determine the amount of memory
- needed for the interface table.
-
-*******************************************************************************/
-
-static s4 class_highestinterface(classinfo *c)
-{
- s4 h;
- s4 h2;
- s4 i;
-
- /* check for ACC_INTERFACE bit already done in link_class_intern */
-
- h = c->index;
-
- for (i = 0; i < c->interfacescount; i++) {
- h2 = class_highestinterface(c->interfaces[i]);
-
- if (h2 > h)
- h = h2;
- }
-
- return h;
-}
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/linker.c - class linker functions
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "native/native.hpp"
+
+#include "threads/lock.hpp"
+#include "threads/mutex.hpp"
+
+#include "toolbox/logging.h"
+
+#include "vm/access.hpp"
+#include "vm/array.hpp"
+#include "vm/class.hpp"
+#include "vm/classcache.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/globals.hpp"
+#include "vm/loader.hpp"
+#include "vm/options.h"
+#include "vm/primitive.hpp"
+#include "vm/rt-timing.h"
+#include "vm/string.hpp"
+#include "vm/vm.hpp"
+
+#include "vm/jit/asmpart.h"
+#include "vm/jit/stubs.hpp"
+
+
+/* debugging macros ***********************************************************/
+
+#if !defined(NDEBUG)
+# define TRACELINKCLASS(c) \
+ do { \
+ if (opt_TraceLinkClass) { \
+ log_start(); \
+ log_print("[Linking "); \
+ class_print((c)); \
+ log_print("]"); \
+ log_finish(); \
+ } \
+ } while (0)
+#else
+# define TRACELINKCLASS(c)
+#endif
+
+
+/* #include "vm/resolve.hpp" */
+/* copied prototype to avoid bootstrapping problem: */
+classinfo *resolve_classref_or_classinfo_eager(classref_or_classinfo cls, bool checkaccess);
+
+#if defined(ENABLE_STATISTICS)
+# include "vm/statistics.h"
+#endif
+
+#if !defined(NDEBUG) && defined(ENABLE_INLINING)
+#define INLINELOG(code) do { if (opt_TraceInlining) { code } } while (0)
+#else
+#define INLINELOG(code)
+#endif
+
+
+/* global variables ***********************************************************/
+
+static s4 interfaceindex; /* sequential numbering of interfaces */
+static s4 classvalue;
+
+Mutex *linker_classrenumber_mutex;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* private functions **********************************************************/
+
+static classinfo *link_class_intern(classinfo *c);
+static arraydescriptor *link_array(classinfo *c);
+static void linker_compute_class_values(classinfo *c);
+static void linker_compute_subclasses(classinfo *c);
+static bool linker_addinterface(classinfo *c, classinfo *ic);
+static s4 class_highestinterface(classinfo *c);
+
+
+/* linker_init *****************************************************************
+
+ Initializes the linker subsystem and links classes required for the
+ primitive table.
+
+*******************************************************************************/
+
+void linker_preinit(void)
+{
+ TRACESUBSYSTEMINITIALIZATION("linker_preinit");
+
+ /* Reset interface index. */
+
+ interfaceindex = 0;
+
+#if defined(ENABLE_THREADS)
+ /* create the global mutex */
+
+ linker_classrenumber_mutex = new Mutex();
+#endif
+
+ /* Link the most basic classes. */
+
+ if (!link_class(class_java_lang_Object))
+ vm_abort("linker_preinit: linking java/lang/Object failed");
+
+#if defined(ENABLE_JAVASE)
+ if (!link_class(class_java_lang_Cloneable))
+ vm_abort("linker_preinit: linking java/lang/Cloneable failed");
+
+ if (!link_class(class_java_io_Serializable))
+ vm_abort("linker_preinit: linking java/io/Serializable failed");
+#endif
+}
+
+
+/* linker_init *****************************************************************
+
+ Links all classes required in the VM.
+
+*******************************************************************************/
+
+void linker_init(void)
+{
+ TRACESUBSYSTEMINITIALIZATION("linker_init");
+
+ /* Link java.lang.Class as first class of the system, because we
+ need it's vftbl for all other classes so we can use a class as
+ object. */
+
+ if (!link_class(class_java_lang_Class))
+ vm_abort("linker_init: linking java/lang/Class failed");
+
+ /* Now set the header.vftbl of all classes which were created
+ before java.lang.Class was linked. */
+
+ class_postset_header_vftbl();
+
+ /* Link primitive-type wrapping classes. */
+
+#if defined(ENABLE_JAVASE)
+ if (!link_class(class_java_lang_Void))
+ vm_abort("linker_init: linking failed");
+#endif
+
+ if (!link_class(class_java_lang_Boolean))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Byte))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Character))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Short))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Integer))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Long))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Float))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Double))
+ vm_abort("linker_init: linking failed");
+
+ /* Link important system classes. */
+
+ if (!link_class(class_java_lang_String))
+ vm_abort("linker_init: linking java/lang/String failed");
+
+#if defined(ENABLE_JAVASE)
+ if (!link_class(class_java_lang_ClassLoader))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_SecurityManager))
+ vm_abort("linker_init: linking failed");
+#endif
+
+ if (!link_class(class_java_lang_System))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_Thread))
+ vm_abort("linker_init: linking failed");
+
+#if defined(ENABLE_JAVASE)
+ if (!link_class(class_java_lang_ThreadGroup))
+ vm_abort("linker_init: linking failed");
+#endif
+
+ if (!link_class(class_java_lang_Throwable))
+ vm_abort("linker_init: linking failed");
+
+#if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
+ if (!link_class(class_java_lang_VMSystem))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_VMThread))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_VMThrowable))
+ vm_abort("linker_init: linking failed");
+#endif
+
+ /* Important system exceptions. */
+
+ if (!link_class(class_java_lang_Exception))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_ClassNotFoundException))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_RuntimeException))
+ vm_abort("linker_init: linking failed");
+
+ /* some classes which may be used more often */
+
+#if defined(ENABLE_JAVASE)
+ if (!link_class(class_java_lang_StackTraceElement))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_reflect_Constructor))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_reflect_Field))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_reflect_Method))
+ vm_abort("linker_init: linking failed");
+
+# if defined(WITH_JAVA_RUNTIME_LIBRARY_GNU_CLASSPATH)
+ if (!link_class(class_java_lang_reflect_VMConstructor))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_reflect_VMField))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_lang_reflect_VMMethod))
+ vm_abort("linker_init: linking failed");
+# endif
+
+ if (!link_class(class_java_security_PrivilegedAction))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_util_Vector))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_java_util_HashMap))
+ vm_abort("linker_init: linking failed");
+
+# if defined(WITH_JAVA_RUNTIME_LIBRARY_OPENJDK)
+ if (!link_class(class_sun_misc_Signal))
+ vm_abort("linker_init: linking failed");
+
+ if (!link_class(class_sun_reflect_MagicAccessorImpl))
+ vm_abort("linker_init: linking failed");
+# endif
+
+ if (!link_class(arrayclass_java_lang_Object))
+ vm_abort("linker_init: linking failed");
+#endif
+
+
+ /* create pseudo classes used by the typechecker */
+
+ /* pseudo class for Arraystubs (extends java.lang.Object) */
+
+ pseudo_class_Arraystub =
+ class_create_classinfo(utf_new_char("$ARRAYSTUB$"));
+ pseudo_class_Arraystub->state |= CLASS_LOADED;
+ pseudo_class_Arraystub->super = class_java_lang_Object;
+
+#if defined(ENABLE_JAVASE)
+
+ pseudo_class_Arraystub->interfacescount = 2;
+ pseudo_class_Arraystub->interfaces = MNEW(classinfo*, 2);
+ pseudo_class_Arraystub->interfaces[0] = class_java_lang_Cloneable;
+ pseudo_class_Arraystub->interfaces[1] = class_java_io_Serializable;
+
+#elif defined(ENABLE_JAVAME_CLDC1_1)
+
+ pseudo_class_Arraystub->interfacescount = 0;
+ pseudo_class_Arraystub->interfaces = NULL;
+
+#else
+# error unknown Java configuration
+#endif
+
+ if (!classcache_store_unique(pseudo_class_Arraystub))
+ vm_abort("linker_init: could not cache pseudo_class_Arraystub");
+
+ if (!link_class(pseudo_class_Arraystub))
+ vm_abort("linker_init: linking pseudo_class_Arraystub failed");
+
+ /* pseudo class representing the null type */
+
+ pseudo_class_Null = class_create_classinfo(utf_new_char("$NULL$"));
+ pseudo_class_Null->state |= CLASS_LOADED;
+ pseudo_class_Null->super = class_java_lang_Object;
+
+ if (!classcache_store_unique(pseudo_class_Null))
+ vm_abort("linker_init: could not cache pseudo_class_Null");
+
+ if (!link_class(pseudo_class_Null))
+ vm_abort("linker_init: linking failed");
+
+ /* pseudo class representing new uninitialized objects */
+
+ pseudo_class_New = class_create_classinfo(utf_new_char("$NEW$"));
+ pseudo_class_New->state |= CLASS_LOADED;
+ pseudo_class_New->state |= CLASS_LINKED; /* XXX is this allright? */
+ pseudo_class_New->super = class_java_lang_Object;
+
+ if (!classcache_store_unique(pseudo_class_New))
+ vm_abort("linker_init: could not cache pseudo_class_New");
+
+ /* Correct vftbl-entries (retarded loading and linking of class
+ java/lang/String). */
+
+ stringtable_update();
+}
+
+
+/* link_class ******************************************************************
+
+ Wrapper function for link_class_intern to ease monitor enter/exit
+ and exception handling.
+
+*******************************************************************************/
+
+classinfo *link_class(classinfo *c)
+{
+ classinfo *r;
+#if defined(ENABLE_RT_TIMING)
+ struct timespec time_start, time_end;
+#endif
+
+ RT_TIMING_GET_TIME(time_start);
+
+ if (c == NULL) {
+ exceptions_throw_nullpointerexception();
+ return NULL;
+ }
+
+ LOCK_MONITOR_ENTER(c);
+
+ /* Maybe the class is currently linking or is already linked.*/
+
+ if ((c->state & CLASS_LINKING) || (c->state & CLASS_LINKED)) {
+ LOCK_MONITOR_EXIT(c);
+
+ return c;
+ }
+
+#if defined(ENABLE_STATISTICS)
+ /* measure time */
+
+ if (opt_getcompilingtime)
+ compilingtime_stop();
+
+ if (opt_getloadingtime)
+ loadingtime_start();
+#endif
+
+ /* call the internal function */
+
+ r = link_class_intern(c);
+
+ /* If return value is NULL, we had a problem and the class is not
+ linked. */
+
+ if (r == NULL)
+ c->state &= ~CLASS_LINKING;
+
+#if defined(ENABLE_STATISTICS)
+ /* measure time */
+
+ if (opt_getloadingtime)
+ loadingtime_stop();
+
+ if (opt_getcompilingtime)
+ compilingtime_start();
+#endif
+
+ LOCK_MONITOR_EXIT(c);
+
+ RT_TIMING_GET_TIME(time_end);
+
+ RT_TIMING_TIME_DIFF(time_start,time_end,RT_TIMING_LINK_TOTAL);
+
+ return r;
+}
+
+
+/* linker_overwrite_method *****************************************************
+
+ Overwrite a method with another one, update method flags and check
+ assumptions.
+
+ IN:
+ mg................the general method being overwritten
+ ms................the overwriting (more specialized) method
+ wl................worklist where to add invalidated methods
+
+ RETURN VALUE:
+ true..............everything ok
+ false.............an exception has been thrown
+
+*******************************************************************************/
+
+static bool linker_overwrite_method(methodinfo *mg,
+ methodinfo *ms,
+ method_worklist **wl)
+{
+ classinfo *cg;
+ classinfo *cs;
+
+ cg = mg->clazz;
+ cs = ms->clazz;
+
+ /* overriding a final method is illegal */
+
+ if (mg->flags & ACC_FINAL) {
+ exceptions_throw_verifyerror(mg, "Overriding final method");
+ return false;
+ }
+
+ /* method ms overwrites method mg */
+
+#if defined(ENABLE_VERIFIER)
+ /* Add loading constraints (for the more general types of method mg). */
+ /* Not for <init>, as it is not invoked virtually. */
+
+ if ((ms->name != utf_init)
+ && !classcache_add_constraints_for_params(
+ cs->classloader, cg->classloader, mg))
+ {
+ return false;
+ }
+#endif
+
+ /* inherit the vftbl index, and record the overwriting */
+
+ ms->vftblindex = mg->vftblindex;
+ ms->overwrites = mg;
+
+ /* update flags and check assumptions */
+ /* <init> methods are a special case, as they are never dispatched dynamically */
+
+ if ((ms->flags & ACC_METHOD_IMPLEMENTED) && ms->name != utf_init) {
+ do {
+
+#if defined(ENABLE_TLH)
+ if (mg->flags & ACC_METHOD_MONOMORPHY_USED) {
+ printf("%s/%s is evil! the siner is %s/%s\n", mg->clazz->name->text, mg->name->text,
+ ms->clazz->name->text, ms->name->text);
+ ms->flags |= ACC_METHOD_PARENT_MONOMORPHY_USED;
+ }
+#endif
+
+ if (mg->flags & ACC_METHOD_IMPLEMENTED) {
+ /* this adds another implementation */
+
+ mg->flags &= ~ACC_METHOD_MONOMORPHIC;
+
+ INLINELOG( printf("becomes polymorphic: "); method_println(mg); );
+
+ method_break_assumption_monomorphic(mg, wl);
+ }
+ else {
+ /* this is the first implementation */
+
+ mg->flags |= ACC_METHOD_IMPLEMENTED;
+
+ INLINELOG( printf("becomes implemented: "); method_println(mg); );
+ }
+
+ ms = mg;
+ mg = mg->overwrites;
+ } while (mg != NULL);
+ }
+
+ return true;
+}
+
+
+/* link_class_intern ***********************************************************
+
+ Tries to link a class. The function calculates the length in bytes
+ that an instance of this class requires as well as the VTBL for
+ methods and interface methods.
+
+*******************************************************************************/
+
+static classinfo *link_class_intern(classinfo *c)
+{
+ classinfo *super; /* super class */
+ classinfo *tc; /* temporary class variable */
+ s4 supervftbllength; /* vftbllegnth of super class */
+ s4 vftbllength; /* vftbllength of current class */
+ s4 interfacetablelength; /* interface table length */
+ vftbl_t *v; /* vftbl of current class */
+ s4 i; /* interface/method/field counter */
+ arraydescriptor *arraydesc; /* descriptor for array classes */
+ method_worklist *worklist; /* worklist for recompilation */
+#if defined(ENABLE_RT_TIMING)
+ struct timespec time_start, time_resolving, time_compute_vftbl,
+ time_abstract, time_compute_iftbl, time_fill_vftbl,
+ time_offsets, time_fill_iftbl, time_finalizer,
+ time_subclasses;
+#endif
+
+ RT_TIMING_GET_TIME(time_start);
+
+ TRACELINKCLASS(c);
+
+ /* the class must be loaded */
+
+ /* XXX should this be a specific exception? */
+ assert(c->state & CLASS_LOADED);
+
+ /* This is check in link_class. */
+
+ assert(!(c->state & CLASS_LINKED));
+
+ /* cache the self-reference of this class */
+ /* we do this for cases where the defining loader of the class */
+ /* has not yet been recorded as an initiating loader for the class */
+ /* this is needed so subsequent code can assume that self-refs */
+ /* will always resolve lazily */
+ /* No need to do it for the bootloader - it is always registered */
+ /* as initiating loader for the classes it loads. */
+ if (c->classloader)
+ classcache_store(c->classloader,c,false);
+
+ /* this class is currently linking */
+
+ c->state |= CLASS_LINKING;
+
+ arraydesc = NULL;
+ worklist = NULL;
+
+ /* Link the super interfaces. */
+
+ for (i = 0; i < c->interfacescount; i++) {
+ tc = c->interfaces[i];
+
+ if (!(tc->state & CLASS_LINKED))
+ if (!link_class(tc))
+ return NULL;
+ }
+
+ /* check super class */
+
+ super = NULL;
+
+ /* Check for java/lang/Object. */
+
+ if (c->super == NULL) {
+ c->index = 0;
+ c->instancesize = sizeof(java_object_t);
+
+ vftbllength = supervftbllength = 0;
+
+ c->finalizer = NULL;
+ }
+ else {
+ /* Get super class. */
+
+ super = c->super;
+
+ /* Link the super class if necessary. */
+
+ if (!(super->state & CLASS_LINKED))
+ if (!link_class(super))
+ return NULL;
+
+ /* OR the ACC_CLASS_HAS_POINTERS and the ACC_CLASS_REFERENCE_*
+ flags. */
+
+ c->flags |= (super->flags &
+ (ACC_CLASS_HAS_POINTERS | ACC_CLASS_REFERENCE_MASK));
+
+ /* handle array classes */
+
+ if (c->name->text[0] == '[')
+ if (!(arraydesc = link_array(c)))
+ return NULL;
+
+ if (c->flags & ACC_INTERFACE)
+ c->index = interfaceindex++;
+ else
+ c->index = super->index + 1;
+
+ c->instancesize = super->instancesize;
+
+ vftbllength = supervftbllength = super->vftbl->vftbllength;
+
+ c->finalizer = super->finalizer;
+ }
+ RT_TIMING_GET_TIME(time_resolving);
+
+
+ /* compute vftbl length */
+
+ for (i = 0; i < c->methodscount; i++) {
+ methodinfo *m = &(c->methods[i]);
+
+ if (!(m->flags & ACC_STATIC)) { /* is instance method */
+ tc = super;
+
+ while (tc) {
+ s4 j;
+
+ for (j = 0; j < tc->methodscount; j++) {
+ if (method_canoverwrite(m, &(tc->methods[j]))) {
+ if (tc->methods[j].flags & ACC_PRIVATE)
+ goto notfoundvftblindex;
+
+ /* package-private methods in other packages */
+ /* must not be overridden */
+ /* (see Java Language Specification 8.4.8.1) */
+ if ( !(tc->methods[j].flags & (ACC_PUBLIC | ACC_PROTECTED))
+ && !SAME_PACKAGE(c,tc) )
+ {
+ goto notfoundvftblindex;
+ }
+
+ if (!linker_overwrite_method(&(tc->methods[j]), m, &worklist))
+ return NULL;
+
+ goto foundvftblindex;
+ }
+ }
+
+ tc = tc->super;
+ }
+
+ notfoundvftblindex:
+ m->vftblindex = (vftbllength++);
+ foundvftblindex:
+ ;
+ }
+ }
+ RT_TIMING_GET_TIME(time_compute_vftbl);
+
+
+ /* Check all interfaces of an abstract class (maybe be an
+ interface too) for unimplemented methods. Such methods are
+ called miranda-methods and are marked with the ACC_MIRANDA
+ flag. VMClass.getDeclaredMethods does not return such
+ methods. */
+
+ if (c->flags & ACC_ABSTRACT) {
+ classinfo *ic;
+ methodinfo *im;
+ s4 abstractmethodscount;
+ s4 j;
+ s4 k;
+
+ abstractmethodscount = 0;
+
+ /* check all interfaces of the abstract class */
+
+ for (i = 0; i < c->interfacescount; i++) {
+ ic = c->interfaces[i];
+
+ for (j = 0; j < ic->methodscount; j++) {
+ im = &(ic->methods[j]);
+
+ /* skip `<clinit>' and `<init>' */
+
+ if ((im->name == utf_clinit) || (im->name == utf_init))
+ continue;
+
+ for (tc = c; tc != NULL; tc = tc->super) {
+ for (k = 0; k < tc->methodscount; k++) {
+ if (method_canoverwrite(im, &(tc->methods[k])))
+ goto noabstractmethod;
+ }
+ }
+
+ abstractmethodscount++;
+
+ noabstractmethod:
+ ;
+ }
+ }
+
+ if (abstractmethodscount > 0) {
+ methodinfo *am;
+
+ /* reallocate methods memory */
+
+ c->methods = (methodinfo*) MREALLOC(c->methods, methodinfo, c->methodscount,
+ c->methodscount + abstractmethodscount);
+
+ for (i = 0; i < c->interfacescount; i++) {
+ ic = c->interfaces[i];
+
+ for (j = 0; j < ic->methodscount; j++) {
+ im = &(ic->methods[j]);
+
+ /* skip `<clinit>' and `<init>' */
+
+ if ((im->name == utf_clinit) || (im->name == utf_init))
+ continue;
+
+ for (tc = c; tc != NULL; tc = tc->super) {
+ for (k = 0; k < tc->methodscount; k++) {
+ if (method_canoverwrite(im, &(tc->methods[k])))
+ goto noabstractmethod2;
+ }
+ }
+
+ /* Copy the method found into the new c->methods
+ array and tag it as miranda-method. */
+
+ am = &(c->methods[c->methodscount]);
+ c->methodscount++;
+
+ MCOPY(am, im, methodinfo, 1);
+
+ am->vftblindex = (vftbllength++);
+ am->clazz = c;
+ am->flags |= ACC_MIRANDA;
+
+ noabstractmethod2:
+ ;
+ }
+ }
+ }
+ }
+ RT_TIMING_GET_TIME(time_abstract);
+
+
+#if defined(ENABLE_STATISTICS)
+ if (opt_stat)
+ count_vftbl_len +=
+ sizeof(vftbl_t) + (sizeof(methodptr) * (vftbllength - 1));
+#endif
+
+ /* compute interfacetable length */
+
+ interfacetablelength = 0;
+
+ for (tc = c; tc != NULL; tc = tc->super) {
+ for (i = 0; i < tc->interfacescount; i++) {
+ s4 h = class_highestinterface(tc->interfaces[i]) + 1;
+
+ if (h > interfacetablelength)
+ interfacetablelength = h;
+ }
+ }
+ RT_TIMING_GET_TIME(time_compute_iftbl);
+
+ /* allocate virtual function table */
+
+ v = (vftbl_t *) mem_alloc(sizeof(vftbl_t) +
+ sizeof(methodptr) * (vftbllength - 1) +
+ sizeof(methodptr*) * (interfacetablelength - (interfacetablelength > 0)));
+ v = (vftbl_t *) (((methodptr *) v) +
+ (interfacetablelength - 1) * (interfacetablelength > 1));
+
+ c->vftbl = v;
+ v->clazz = c;
+ v->vftbllength = vftbllength;
+ v->interfacetablelength = interfacetablelength;
+ v->arraydesc = arraydesc;
+
+ /* store interface index in vftbl */
+
+ if (c->flags & ACC_INTERFACE)
+ v->baseval = -(c->index);
+
+ /* copy virtual function table of super class */
+
+ for (i = 0; i < supervftbllength; i++)
+ v->table[i] = super->vftbl->table[i];
+
+ /* Fill the remaining vftbl slots with the AbstractMethodError
+ stub (all after the super class slots, because they are already
+ initialized). */
+
+ for (; i < vftbllength; i++) {
+#if defined(ENABLE_JIT)
+# if defined(ENABLE_INTRP)
+ if (opt_intrp)
+ v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
+ else
+# endif
+ v->table[i] = (methodptr) (ptrint) &asm_abstractmethoderror;
+#else
+ v->table[i] = (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
+#endif
+ }
+
+ /* add method stubs into virtual function table */
+
+ for (i = 0; i < c->methodscount; i++) {
+ methodinfo *m = &(c->methods[i]);
+
+ assert(m->stubroutine == NULL);
+
+ /* Don't create a compiler stub for abstract methods as they
+ throw an AbstractMethodError with the default stub in the
+ vftbl. This entry is simply copied by sub-classes. */
+
+ if (m->flags & ACC_ABSTRACT)
+ continue;
+
+#if defined(ENABLE_JIT)
+# if defined(ENABLE_INTRP)
+ if (opt_intrp)
+ m->stubroutine = intrp_createcompilerstub(m);
+ else
+#endif
+ m->stubroutine = (u1*) CompilerStub::generate(m);
+#else
+ m->stubroutine = intrp_createcompilerstub(m);
+#endif
+
+ /* static methods are not in the vftbl */
+
+ if (m->flags & ACC_STATIC)
+ continue;
+
+ /* insert the stubroutine into the vftbl */
+
+ v->table[m->vftblindex] = (methodptr) (ptrint) m->stubroutine;
+ }
+ RT_TIMING_GET_TIME(time_fill_vftbl);
+
+ /* compute instance size and offset of each field */
+
+ for (i = 0; i < c->fieldscount; i++) {
+ s4 dsize;
+ fieldinfo *f = &(c->fields[i]);
+
+ if (!(f->flags & ACC_STATIC)) {
+ dsize = descriptor_typesize(f->parseddesc);
+ c->instancesize = MEMORY_ALIGN(c->instancesize, dsize);
+ f->offset = c->instancesize;
+ c->instancesize += dsize;
+ }
+ }
+ RT_TIMING_GET_TIME(time_offsets);
+
+ /* initialize interfacetable and interfacevftbllength */
+
+ v->interfacevftbllength = MNEW(s4, interfacetablelength);
+
+#if defined(ENABLE_STATISTICS)
+ if (opt_stat)
+ count_vftbl_len += (4 + sizeof(s4)) * v->interfacetablelength;
+#endif
+
+ for (i = 0; i < interfacetablelength; i++) {
+ v->interfacevftbllength[i] = 0;
+ v->interfacetable[-i] = NULL;
+ }
+
+ /* add interfaces */
+
+ for (tc = c; tc != NULL; tc = tc->super)
+ for (i = 0; i < tc->interfacescount; i++)
+ if (!linker_addinterface(c, tc->interfaces[i]))
+ return NULL;
+
+ RT_TIMING_GET_TIME(time_fill_iftbl);
+
+ /* add finalizer method (not for java.lang.Object) */
+
+ if (super) {
+ methodinfo *fi;
+
+ fi = class_findmethod(c, utf_finalize, utf_void__void);
+
+ if (fi)
+ if (!(fi->flags & ACC_STATIC))
+ c->finalizer = fi;
+ }
+ RT_TIMING_GET_TIME(time_finalizer);
+
+ /* final tasks */
+
+ linker_compute_subclasses(c);
+
+ RT_TIMING_GET_TIME(time_subclasses);
+
+ /* revert the linking state and class is linked */
+
+ c->state = (c->state & ~CLASS_LINKING) | CLASS_LINKED;
+
+ /* check worklist */
+
+ /* XXX must this also be done in case of exception? */
+
+ while (worklist != NULL) {
+ method_worklist *wi = worklist;
+
+ worklist = worklist->next;
+
+ INLINELOG( printf("MUST BE RECOMPILED: "); method_println(wi->m); );
+ jit_invalidate_code(wi->m);
+
+ /* XXX put worklist into dump memory? */
+ FREE(wi, method_worklist);
+ }
+
+ RT_TIMING_TIME_DIFF(time_start ,time_resolving ,RT_TIMING_LINK_RESOLVE);
+ RT_TIMING_TIME_DIFF(time_resolving ,time_compute_vftbl,RT_TIMING_LINK_C_VFTBL);
+ RT_TIMING_TIME_DIFF(time_compute_vftbl,time_abstract ,RT_TIMING_LINK_ABSTRACT);
+ RT_TIMING_TIME_DIFF(time_abstract ,time_compute_iftbl,RT_TIMING_LINK_C_IFTBL);
+ RT_TIMING_TIME_DIFF(time_compute_iftbl,time_fill_vftbl ,RT_TIMING_LINK_F_VFTBL);
+ RT_TIMING_TIME_DIFF(time_fill_vftbl ,time_offsets ,RT_TIMING_LINK_OFFSETS);
+ RT_TIMING_TIME_DIFF(time_offsets ,time_fill_iftbl ,RT_TIMING_LINK_F_IFTBL);
+ RT_TIMING_TIME_DIFF(time_fill_iftbl ,time_finalizer ,RT_TIMING_LINK_FINALIZER);
+ RT_TIMING_TIME_DIFF(time_finalizer ,time_subclasses ,RT_TIMING_LINK_SUBCLASS);
+
+ /* just return c to show that we didn't had a problem */
+
+ return c;
+}
+
+
+/* link_array ******************************************************************
+
+ This function is called by link_class to create the arraydescriptor
+ for an array class.
+
+ This function returns NULL if the array cannot be linked because
+ the component type has not been linked yet.
+
+*******************************************************************************/
+
+static arraydescriptor *link_array(classinfo *c)
+{
+ classinfo *comp;
+ s4 namelen;
+ arraydescriptor *desc;
+ vftbl_t *compvftbl;
+ utf *u;
+
+ comp = NULL;
+ namelen = c->name->blength;
+
+ /* Check the component type */
+
+ switch (c->name->text[1]) {
+ case '[':
+ /* c is an array of arrays. */
+ u = utf_new(c->name->text + 1, namelen - 1);
+ if (!(comp = load_class_from_classloader(u, c->classloader)))
+ return NULL;
+ break;
+
+ case 'L':
+ /* c is an array of objects. */
+ u = utf_new(c->name->text + 2, namelen - 3);
+ if (!(comp = load_class_from_classloader(u, c->classloader)))
+ return NULL;
+ break;
+ }
+
+ /* If the component type has not been linked, link it now */
+
+ assert(!comp || (comp->state & CLASS_LOADED));
+
+ if (comp && !(comp->state & CLASS_LINKED))
+ if (!link_class(comp))
+ return NULL;
+
+ /* Allocate the arraydescriptor */
+
+ desc = NEW(arraydescriptor);
+
+ if (comp) {
+ /* c is an array of references */
+ desc->arraytype = ARRAYTYPE_OBJECT;
+ desc->componentsize = sizeof(void*);
+ desc->dataoffset = OFFSET(java_objectarray_t, data);
+
+ compvftbl = comp->vftbl;
+
+ if (!compvftbl) {
+ log_text("Component class has no vftbl");
+ assert(0);
+ }
+
+ desc->componentvftbl = compvftbl;
+
+ if (compvftbl->arraydesc) {
+ desc->elementvftbl = compvftbl->arraydesc->elementvftbl;
+
+ if (compvftbl->arraydesc->dimension >= 255) {
+ exceptions_throw_illegalargumentexception();
+ return NULL;
+ }
+
+ desc->dimension = compvftbl->arraydesc->dimension + 1;
+ desc->elementtype = compvftbl->arraydesc->elementtype;
+
+ } else {
+ desc->elementvftbl = compvftbl;
+ desc->dimension = 1;
+ desc->elementtype = ARRAYTYPE_OBJECT;
+ }
+
+ } else {
+ /* c is an array of a primitive type */
+ switch (c->name->text[1]) {
+ case 'Z':
+ desc->arraytype = ARRAYTYPE_BOOLEAN;
+ desc->dataoffset = OFFSET(java_booleanarray_t,data);
+ desc->componentsize = sizeof(u1);
+ break;
+
+ case 'B':
+ desc->arraytype = ARRAYTYPE_BYTE;
+ desc->dataoffset = OFFSET(java_bytearray_t,data);
+ desc->componentsize = sizeof(u1);
+ break;
+
+ case 'C':
+ desc->arraytype = ARRAYTYPE_CHAR;
+ desc->dataoffset = OFFSET(java_chararray_t,data);
+ desc->componentsize = sizeof(u2);
+ break;
+
+ case 'D':
+ desc->arraytype = ARRAYTYPE_DOUBLE;
+ desc->dataoffset = OFFSET(java_doublearray_t,data);
+ desc->componentsize = sizeof(double);
+ break;
+
+ case 'F':
+ desc->arraytype = ARRAYTYPE_FLOAT;
+ desc->dataoffset = OFFSET(java_floatarray_t,data);
+ desc->componentsize = sizeof(float);
+ break;
+
+ case 'I':
+ desc->arraytype = ARRAYTYPE_INT;
+ desc->dataoffset = OFFSET(java_intarray_t,data);
+ desc->componentsize = sizeof(s4);
+ break;
+
+ case 'J':
+ desc->arraytype = ARRAYTYPE_LONG;
+ desc->dataoffset = OFFSET(java_longarray_t,data);
+ desc->componentsize = sizeof(s8);
+ break;
+
+ case 'S':
+ desc->arraytype = ARRAYTYPE_SHORT;
+ desc->dataoffset = OFFSET(java_shortarray_t,data);
+ desc->componentsize = sizeof(s2);
+ break;
+
+ default:
+ exceptions_throw_noclassdeffounderror(c->name);
+ return NULL;
+ }
+
+ desc->componentvftbl = NULL;
+ desc->elementvftbl = NULL;
+ desc->dimension = 1;
+ desc->elementtype = desc->arraytype;
+ }
+
+ return desc;
+}
+
+
+/* linker_compute_subclasses ***************************************************
+
+ XXX
+
+ ATTENTION: DO NOT REMOVE ANY OF THE LOCKING MECHANISMS BELOW:
+ This function needs to take the class renumber lock and stop the
+ world during class renumbering. The lock is used in C code which
+ is not that performance critical. Whereas JIT code uses critical
+ sections to atomically access the class values.
+
+*******************************************************************************/
+
+static void linker_compute_subclasses(classinfo *c)
+{
+ linker_classrenumber_mutex->lock();
+
+ if (!(c->flags & ACC_INTERFACE)) {
+ c->nextsub = NULL;
+ c->sub = NULL;
+ }
+
+ if (!(c->flags & ACC_INTERFACE) && (c->super != NULL)) {
+ c->nextsub = c->super->sub;
+ c->super->sub = c;
+ }
+
+ classvalue = 0;
+
+ /* compute class values */
+
+ linker_compute_class_values(class_java_lang_Object);
+
+ linker_classrenumber_mutex->unlock();
+}
+
+
+/* linker_compute_class_values *************************************************
+
+ XXX
+
+*******************************************************************************/
+
+static void linker_compute_class_values(classinfo *c)
+{
+ classinfo *subs;
+
+ c->vftbl->baseval = ++classvalue;
+
+ subs = c->sub;
+
+ while (subs) {
+ linker_compute_class_values(subs);
+
+ subs = subs->nextsub;
+ }
+
+ c->vftbl->diffval = classvalue - c->vftbl->baseval;
+}
+
+
+/* linker_addinterface *********************************************************
+
+ Is needed by link_class for adding a VTBL to a class. All
+ interfaces implemented by ic are added as well.
+
+ RETURN VALUE:
+ true.........everything ok
+ false........an exception has been thrown
+
+*******************************************************************************/
+
+static bool linker_addinterface(classinfo *c, classinfo *ic)
+{
+ s4 j, k;
+ vftbl_t *v;
+ s4 i;
+ classinfo *sc;
+ methodinfo *m;
+
+ v = c->vftbl;
+ i = ic->index;
+
+ if (i >= v->interfacetablelength)
+ vm_abort("Internal error: interfacetable overflow");
+
+ /* if this interface has already been added, return immediately */
+
+ if (v->interfacetable[-i] != NULL)
+ return true;
+
+ if (ic->methodscount == 0) { /* fake entry needed for subtype test */
+ v->interfacevftbllength[i] = 1;
+ v->interfacetable[-i] = MNEW(methodptr, 1);
+ v->interfacetable[-i][0] = NULL;
+ }
+ else {
+ v->interfacevftbllength[i] = ic->methodscount;
+ v->interfacetable[-i] = MNEW(methodptr, ic->methodscount);
+
+#if defined(ENABLE_STATISTICS)
+ if (opt_stat)
+ count_vftbl_len += sizeof(methodptr) *
+ (ic->methodscount + (ic->methodscount == 0));
+#endif
+
+ for (j = 0; j < ic->methodscount; j++) {
+ for (sc = c; sc != NULL; sc = sc->super) {
+ for (k = 0; k < sc->methodscount; k++) {
+ m = &(sc->methods[k]);
+
+ if (method_canoverwrite(m, &(ic->methods[j]))) {
+ /* method m overwrites the (abstract) method */
+#if defined(ENABLE_VERIFIER)
+ /* Add loading constraints (for the more
+ general types of the method
+ ic->methods[j]). */
+ if (!classcache_add_constraints_for_params(
+ c->classloader, ic->classloader,
+ &(ic->methods[j])))
+ {
+ return false;
+ }
+#endif
+
+ /* XXX taken from gcj */
+ /* check for ACC_STATIC: IncompatibleClassChangeError */
+
+ /* check for !ACC_PUBLIC: IllegalAccessError */
+
+ /* check for ACC_ABSTRACT: AbstracMethodError,
+ not sure about that one */
+
+ v->interfacetable[-i][j] = v->table[m->vftblindex];
+ goto foundmethod;
+ }
+ }
+ }
+
+ /* If no method was found, insert the AbstractMethodError
+ stub. */
+
+#if defined(ENABLE_JIT)
+# if defined(ENABLE_INTRP)
+ if (opt_intrp)
+ v->interfacetable[-i][j] =
+ (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
+ else
+# endif
+ v->interfacetable[-i][j] =
+ (methodptr) (ptrint) &asm_abstractmethoderror;
+#else
+ v->interfacetable[-i][j] =
+ (methodptr) (ptrint) &intrp_asm_abstractmethoderror;
+#endif
+
+ foundmethod:
+ ;
+ }
+ }
+
+ /* add superinterfaces of this interface */
+
+ for (j = 0; j < ic->interfacescount; j++)
+ if (!linker_addinterface(c, ic->interfaces[j]))
+ return false;
+
+ /* everything ok */
+
+ return true;
+}
+
+
+/* class_highestinterface ******************************************************
+
+ Used by the function link_class to determine the amount of memory
+ needed for the interface table.
+
+*******************************************************************************/
+
+static s4 class_highestinterface(classinfo *c)
+{
+ s4 h;
+ s4 h2;
+ s4 i;
+
+ /* check for ACC_INTERFACE bit already done in link_class_intern */
+
+ h = c->index;
+
+ for (i = 0; i < c->interfacescount; i++) {
+ h2 = class_highestinterface(c->interfaces[i]);
+
+ if (h2 > h)
+ h = h2;
+ }
+
+ return h;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/linker.h - class linker header
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _LINKER_H
-#define _LINKER_H
-
-/* forward typedefs ***********************************************************/
-
-typedef struct arraydescriptor arraydescriptor;
-typedef struct primitivetypeinfo primitivetypeinfo;
-
-
-#include "config.h"
-#include "vm/types.h"
-
-#include "threads/mutex.hpp"
-
-#include "vm/class.hpp"
-#include "vm/references.h"
-#include "vm/vftbl.hpp"
-
-
-/* arraydescriptor *************************************************************
-
- For every array class an arraydescriptor is allocated which
- describes the array class. The arraydescriptor is referenced from
- the vftbl of the array class.
-
-*******************************************************************************/
-
-struct arraydescriptor {
- vftbl_t *componentvftbl; /* vftbl of the component type, NULL for primit. */
- vftbl_t *elementvftbl; /* vftbl of the element type, NULL for primitive */
- s2 arraytype; /* ARRAYTYPE_* constant */
- s2 dimension; /* dimension of the array (always >= 1) */
- s4 dataoffset; /* offset of the array data from object pointer */
- s4 componentsize; /* size of a component in bytes */
- s2 elementtype; /* ARRAYTYPE_* constant */
-};
-
-
-/* global variables ***********************************************************/
-
-/* This lock must be taken while renumbering classes or while atomically */
-/* accessing classes. */
-
-extern Mutex *linker_classrenumber_mutex;
-
-
-/* function prototypes ********************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void linker_preinit(void);
-void linker_init(void);
-classinfo *link_class(classinfo *c);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LINKER_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
--- /dev/null
+/* src/vm/linker.h - class linker header
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _LINKER_H
+#define _LINKER_H
+
+/* forward typedefs ***********************************************************/
+
+typedef struct arraydescriptor arraydescriptor;
+typedef struct primitivetypeinfo primitivetypeinfo;
+
+
+#include "config.h"
+#include "vm/types.h"
+
+#include "threads/mutex.hpp"
+
+#include "vm/class.hpp"
+#include "vm/references.h"
+#include "vm/vftbl.hpp"
+
+
+/* arraydescriptor *************************************************************
+
+ For every array class an arraydescriptor is allocated which
+ describes the array class. The arraydescriptor is referenced from
+ the vftbl of the array class.
+
+*******************************************************************************/
+
+struct arraydescriptor {
+ vftbl_t *componentvftbl; /* vftbl of the component type, NULL for primit. */
+ vftbl_t *elementvftbl; /* vftbl of the element type, NULL for primitive */
+ s2 arraytype; /* ARRAYTYPE_* constant */
+ s2 dimension; /* dimension of the array (always >= 1) */
+ s4 dataoffset; /* offset of the array data from object pointer */
+ s4 componentsize; /* size of a component in bytes */
+ s2 elementtype; /* ARRAYTYPE_* constant */
+};
+
+
+/* global variables ***********************************************************/
+
+/* This lock must be taken while renumbering classes or while atomically */
+/* accessing classes. */
+
+extern Mutex *linker_classrenumber_mutex;
+
+
+/* function prototypes ********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void linker_preinit(void);
+void linker_init(void);
+classinfo *link_class(classinfo *c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LINKER_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
#include "toolbox/logging.h"
#include "vm/jit/builtin.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/exceptions.hpp"
#include "vm/field.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/package.hpp"
#include "vm/primitive.hpp"
#endif
#if defined(ENABLE_ZLIB)
-# include "vm/zip.h"
+# include "vm/zip.hpp"
#endif
#include "vm/jit/stubs.hpp"
#include "vm/types.h"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/class.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/references.h"
#include "vm/utf8.h"
+++ /dev/null
-/* src/vm/method.c - method functions
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include "vm/types.h"
-
-#include "mm/memory.h"
-
-#include "native/llni.h"
-
-#include "threads/mutex.hpp"
-
-#include "vm/array.hpp"
-#include "vm/jit/builtin.hpp"
-#include "vm/class.hpp"
-#include "vm/exceptions.hpp"
-#include "vm/global.h"
-#include "vm/globals.hpp"
-#include "vm/linker.h"
-#include "vm/loader.hpp"
-#include "vm/method.h"
-#include "vm/options.h"
-#include "vm/resolve.hpp"
-#include "vm/suck.hpp"
-#include "vm/utf8.h"
-#include "vm/vm.hpp"
-
-#include "vm/jit/code.hpp"
-#include "vm/jit/methodheader.h"
-#include "vm/jit/stubs.hpp"
-
-
-#if !defined(NDEBUG) && defined(ENABLE_INLINING)
-#define INLINELOG(code) do { if (opt_TraceInlining) { code } } while (0)
-#else
-#define INLINELOG(code)
-#endif
-
-
-/* global variables ***********************************************************/
-
-methodinfo *method_java_lang_reflect_Method_invoke;
-
-
-/* method_init *****************************************************************
-
- Initialize method subsystem.
-
-*******************************************************************************/
-
-void method_init(void)
-{
-#if defined(ENABLE_JAVASE)
- /* Sanity check. */
-
- if (class_java_lang_reflect_Method == NULL)
- vm_abort("method_init: class_java_lang_reflect_Method is NULL");
-
- /* Cache java.lang.reflect.Method.invoke() */
-
- method_java_lang_reflect_Method_invoke =
- class_findmethod(class_java_lang_reflect_Method, utf_invoke, NULL);
-
- if (method_java_lang_reflect_Method_invoke == NULL)
- vm_abort("method_init: Could not resolve method java.lang.reflect.Method.invoke().");
-#endif
-}
-
-
-/* method_load *****************************************************************
-
- Loads a method from the class file and fills an existing methodinfo
- structure.
-
- method_info {
- u2 access_flags;
- u2 name_index;
- u2 descriptor_index;
- u2 attributes_count;
- attribute_info attributes[attribute_count];
- }
-
- attribute_info {
- u2 attribute_name_index;
- u4 attribute_length;
- u1 info[attribute_length];
- }
-
- LineNumberTable_attribute {
- u2 attribute_name_index;
- u4 attribute_length;
- u2 line_number_table_length;
- {
- u2 start_pc;
- u2 line_number;
- } line_number_table[line_number_table_length];
- }
-
-*******************************************************************************/
-
-bool method_load(classbuffer *cb, methodinfo *m, descriptor_pool *descpool)
-{
- classinfo *c;
- int argcount;
- s4 i, j, k, l;
- utf *u;
- u2 name_index;
- u2 descriptor_index;
- u2 attributes_count;
- u2 attribute_name_index;
- utf *attribute_name;
- u2 code_attributes_count;
- u2 code_attribute_name_index;
- utf *code_attribute_name;
-
- /* get classinfo */
-
- c = cb->clazz;
-
- m->mutex = Mutex_new();
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat)
- count_all_methods++;
-#endif
-
- /* all fields of m have been zeroed in load_class_from_classbuffer */
-
- m->clazz = c;
-
- if (!suck_check_classbuffer_size(cb, 2 + 2 + 2))
- return false;
-
- /* access flags */
-
- m->flags = suck_u2(cb);
-
- /* name */
-
- name_index = suck_u2(cb);
-
- if (!(u = class_getconstant(c, name_index, CONSTANT_Utf8)))
- return false;
-
- m->name = u;
-
- /* descriptor */
-
- descriptor_index = suck_u2(cb);
-
- if (!(u = class_getconstant(c, descriptor_index, CONSTANT_Utf8)))
- return false;
-
- m->descriptor = u;
-
- if (!descriptor_pool_add(descpool, u, &argcount))
- return false;
-
-#ifdef ENABLE_VERIFIER
- if (opt_verify) {
- if (!is_valid_name_utf(m->name)) {
- exceptions_throw_classformaterror(c, "Method with invalid name");
- return false;
- }
-
- if (m->name->text[0] == '<' &&
- m->name != utf_init && m->name != utf_clinit) {
- exceptions_throw_classformaterror(c, "Method with invalid special name");
- return false;
- }
- }
-#endif /* ENABLE_VERIFIER */
-
- if (!(m->flags & ACC_STATIC))
- argcount++; /* count the 'this' argument */
-
-#ifdef ENABLE_VERIFIER
- if (opt_verify) {
- if (argcount > 255) {
- exceptions_throw_classformaterror(c, "Too many arguments in signature");
- return false;
- }
-
- /* check flag consistency */
- if (m->name != utf_clinit) {
- i = (m->flags & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED));
-
- if (i != 0 && i != ACC_PUBLIC && i != ACC_PRIVATE && i != ACC_PROTECTED) {
- exceptions_throw_classformaterror(c,
- "Illegal method modifiers: 0x%X",
- m->flags);
- return false;
- }
-
- if (m->flags & ACC_ABSTRACT) {
- if ((m->flags & (ACC_FINAL | ACC_NATIVE | ACC_PRIVATE |
- ACC_STATIC | ACC_STRICT | ACC_SYNCHRONIZED))) {
- exceptions_throw_classformaterror(c,
- "Illegal method modifiers: 0x%X",
- m->flags);
- return false;
- }
- }
-
- if (c->flags & ACC_INTERFACE) {
- if ((m->flags & (ACC_ABSTRACT | ACC_PUBLIC)) != (ACC_ABSTRACT | ACC_PUBLIC)) {
- exceptions_throw_classformaterror(c,
- "Illegal method modifiers: 0x%X",
- m->flags);
- return false;
- }
- }
-
- if (m->name == utf_init) {
- if (m->flags & (ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED |
- ACC_NATIVE | ACC_ABSTRACT)) {
- exceptions_throw_classformaterror(c, "Instance initialization method has invalid flags set");
- return false;
- }
- }
- }
- }
-#endif /* ENABLE_VERIFIER */
-
- /* mark the method as monomorphic until further notice */
-
- m->flags |= ACC_METHOD_MONOMORPHIC;
-
- /* non-abstract methods have an implementation in this class */
-
- if (!(m->flags & ACC_ABSTRACT))
- m->flags |= ACC_METHOD_IMPLEMENTED;
-
- if (!suck_check_classbuffer_size(cb, 2))
- return false;
-
- /* attributes count */
-
- attributes_count = suck_u2(cb);
-
- for (i = 0; i < attributes_count; i++) {
- if (!suck_check_classbuffer_size(cb, 2))
- return false;
-
- /* attribute name index */
-
- attribute_name_index = suck_u2(cb);
-
- attribute_name =
- class_getconstant(c, attribute_name_index, CONSTANT_Utf8);
-
- if (attribute_name == NULL)
- return false;
-
- if (attribute_name == utf_Code) {
- /* Code */
-
- if (m->flags & (ACC_ABSTRACT | ACC_NATIVE)) {
- exceptions_throw_classformaterror(c, "Code attribute in native or abstract methods");
- return false;
- }
-
- if (m->jcode) {
- exceptions_throw_classformaterror(c, "Multiple Code attributes");
- return false;
- }
-
- if (!suck_check_classbuffer_size(cb, 4 + 2 + 2))
- return false;
-
- suck_u4(cb);
- m->maxstack = suck_u2(cb);
- m->maxlocals = suck_u2(cb);
-
- if (m->maxlocals < argcount) {
- exceptions_throw_classformaterror(c, "Arguments can't fit into locals");
- return false;
- }
-
- if (!suck_check_classbuffer_size(cb, 4))
- return false;
-
- m->jcodelength = suck_u4(cb);
-
- if (m->jcodelength == 0) {
- exceptions_throw_classformaterror(c, "Code of a method has length 0");
- return false;
- }
-
- if (m->jcodelength > 65535) {
- exceptions_throw_classformaterror(c, "Code of a method longer than 65535 bytes");
- return false;
- }
-
- if (!suck_check_classbuffer_size(cb, m->jcodelength))
- return false;
-
- m->jcode = MNEW(u1, m->jcodelength);
- suck_nbytes(m->jcode, cb, m->jcodelength);
-
- if (!suck_check_classbuffer_size(cb, 2))
- return false;
-
- m->rawexceptiontablelength = suck_u2(cb);
- if (!suck_check_classbuffer_size(cb, (2 + 2 + 2 + 2) * m->rawexceptiontablelength))
- return false;
-
- m->rawexceptiontable = MNEW(raw_exception_entry, m->rawexceptiontablelength);
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat) {
- count_vmcode_len += m->jcodelength + 18;
- count_extable_len +=
- m->rawexceptiontablelength * sizeof(raw_exception_entry);
- }
-#endif
-
- for (j = 0; j < m->rawexceptiontablelength; j++) {
- u4 idx;
- m->rawexceptiontable[j].startpc = suck_u2(cb);
- m->rawexceptiontable[j].endpc = suck_u2(cb);
- m->rawexceptiontable[j].handlerpc = suck_u2(cb);
-
- idx = suck_u2(cb);
-
- if (!idx) {
- m->rawexceptiontable[j].catchtype.any = NULL;
- }
- else {
- /* the classref is created later */
- if (!(m->rawexceptiontable[j].catchtype.any =
- (utf *) class_getconstant(c, idx, CONSTANT_Class)))
- return false;
- }
- }
-
- if (!suck_check_classbuffer_size(cb, 2))
- return false;
-
- /* code attributes count */
-
- code_attributes_count = suck_u2(cb);
-
- for (k = 0; k < code_attributes_count; k++) {
- if (!suck_check_classbuffer_size(cb, 2))
- return false;
-
- /* code attribute name index */
-
- code_attribute_name_index = suck_u2(cb);
-
- code_attribute_name =
- class_getconstant(c, code_attribute_name_index, CONSTANT_Utf8);
-
- if (code_attribute_name == NULL)
- return false;
-
- /* check which code attribute */
-
- if (code_attribute_name == utf_LineNumberTable) {
- /* LineNumberTable */
-
- if (!suck_check_classbuffer_size(cb, 4 + 2))
- return false;
-
- /* attribute length */
-
- (void) suck_u4(cb);
-
- /* line number table length */
-
- m->linenumbercount = suck_u2(cb);
-
- if (!suck_check_classbuffer_size(cb,
- (2 + 2) * m->linenumbercount))
- return false;
-
- m->linenumbers = MNEW(lineinfo, m->linenumbercount);
-
-#if defined(ENABLE_STATISTICS)
- if (opt_stat)
- size_lineinfo += sizeof(lineinfo) * m->linenumbercount;
-#endif
-
- for (l = 0; l < m->linenumbercount; l++) {
- m->linenumbers[l].start_pc = suck_u2(cb);
- m->linenumbers[l].line_number = suck_u2(cb);
- }
- }
-#if defined(ENABLE_JAVASE)
- else if (code_attribute_name == utf_StackMapTable) {
- /* StackTableMap */
-
- if (!stackmap_load_attribute_stackmaptable(cb, m))
- return false;
- }
-#endif
- else {
- /* unknown code attribute */
-
- if (!loader_skip_attribute_body(cb))
- return false;
- }
- }
- }
- else if (attribute_name == utf_Exceptions) {
- /* Exceptions */
-
- if (m->thrownexceptions != NULL) {
- exceptions_throw_classformaterror(c, "Multiple Exceptions attributes");
- return false;
- }
-
- if (!suck_check_classbuffer_size(cb, 4 + 2))
- return false;
-
- /* attribute length */
-
- (void) suck_u4(cb);
-
- m->thrownexceptionscount = suck_u2(cb);
-
- if (!suck_check_classbuffer_size(cb, 2 * m->thrownexceptionscount))
- return false;
-
- m->thrownexceptions = MNEW(classref_or_classinfo, m->thrownexceptionscount);
-
- for (j = 0; j < m->thrownexceptionscount; j++) {
- /* the classref is created later */
- if (!((m->thrownexceptions)[j].any =
- (utf*) class_getconstant(c, suck_u2(cb), CONSTANT_Class)))
- return false;
- }
- }
-#if defined(ENABLE_JAVASE)
- else if (attribute_name == utf_Signature) {
- /* Signature */
-
- if (!loader_load_attribute_signature(cb, &(m->signature)))
- return false;
- }
-
-#if defined(ENABLE_ANNOTATIONS)
- else if (attribute_name == utf_RuntimeVisibleAnnotations) {
- /* RuntimeVisibleAnnotations */
- if (!annotation_load_method_attribute_runtimevisibleannotations(cb, m))
- return false;
- }
- else if (attribute_name == utf_RuntimeInvisibleAnnotations) {
- /* RuntimeInvisibleAnnotations */
- if (!annotation_load_method_attribute_runtimeinvisibleannotations(cb, m))
- return false;
- }
- else if (attribute_name == utf_RuntimeVisibleParameterAnnotations) {
- /* RuntimeVisibleParameterAnnotations */
- if (!annotation_load_method_attribute_runtimevisibleparameterannotations(cb, m))
- return false;
- }
- else if (attribute_name == utf_RuntimeInvisibleParameterAnnotations) {
- /* RuntimeInvisibleParameterAnnotations */
- if (!annotation_load_method_attribute_runtimeinvisibleparameterannotations(cb, m))
- return false;
- }
- else if (attribute_name == utf_AnnotationDefault) {
- /* AnnotationDefault */
- if (!annotation_load_method_attribute_annotationdefault(cb, m))
- return false;
- }
-#endif
-#endif
- else {
- /* unknown attribute */
-
- if (!loader_skip_attribute_body(cb))
- return false;
- }
- }
-
- if ((m->jcode == NULL) && !(m->flags & (ACC_ABSTRACT | ACC_NATIVE))) {
- exceptions_throw_classformaterror(c, "Missing Code attribute");
- return false;
- }
-
-#if defined(ENABLE_REPLACEMENT)
- /* initialize the hit countdown field */
-
- m->hitcountdown = METHOD_INITIAL_HIT_COUNTDOWN;
-#endif
-
- /* everything was ok */
-
- return true;
-}
-
-
-/* method_free *****************************************************************
-
- Frees all memory that was allocated for this method.
-
-*******************************************************************************/
-
-void method_free(methodinfo *m)
-{
- if (m->mutex)
- Mutex_delete(m->mutex);
-
- if (m->jcode)
- MFREE(m->jcode, u1, m->jcodelength);
-
- if (m->rawexceptiontable)
- MFREE(m->rawexceptiontable, raw_exception_entry, m->rawexceptiontablelength);
-
- code_free_code_of_method(m);
-
- if (m->stubroutine) {
- if (m->flags & ACC_NATIVE) {
- NativeStub_remove(m->stubroutine);
- }
- else {
- CompilerStub_remove(m->stubroutine);
- }
- }
-}
-
-
-/* method_canoverwrite *********************************************************
-
- Check if m and old are identical with respect to type and
- name. This means that old can be overwritten with m.
-
-*******************************************************************************/
-
-bool method_canoverwrite(methodinfo *m, methodinfo *old)
-{
- if (m->name != old->name)
- return false;
-
- if (m->descriptor != old->descriptor)
- return false;
-
- if (m->flags & ACC_STATIC)
- return false;
-
- return true;
-}
-
-
-/* method_new_builtin **********************************************************
-
- Creates a minimal methodinfo structure for builtins. This comes handy
- when dealing with builtin stubs or stacktraces.
-
-*******************************************************************************/
-
-methodinfo *method_new_builtin(builtintable_entry *bte)
-{
- methodinfo *m;
-
- /* allocate the methodinfo structure */
-
- m = NEW(methodinfo);
-
- /* initialize methodinfo structure */
-
- MZERO(m, methodinfo, 1);
-
- m->mutex = Mutex_new();
- m->flags = ACC_METHOD_BUILTIN;
- m->parseddesc = bte->md;
- m->name = bte->name;
- m->descriptor = bte->descriptor;
-
- /* return the newly created methodinfo */
-
- return m;
-}
-
-
-/* method_vftbl_lookup *********************************************************
-
- Does a method lookup in the passed virtual function table. This
- function does exactly the same thing as JIT, but additionally
- relies on the fact, that the methodinfo pointer is at the first
- data segment slot (even for compiler stubs).
-
-*******************************************************************************/
-
-methodinfo *method_vftbl_lookup(vftbl_t *vftbl, methodinfo* m)
-{
- methodptr mptr;
- methodptr *pmptr;
- methodinfo *resm; /* pointer to new resolved method */
-
- /* If the method is not an instance method, just return it. */
-
- if (m->flags & ACC_STATIC)
- return m;
-
- assert(vftbl);
-
- /* Get the method from the virtual function table. Is this an
- interface method? */
-
- if (m->clazz->flags & ACC_INTERFACE) {
- pmptr = vftbl->interfacetable[-(m->clazz->index)];
- mptr = pmptr[(m - m->clazz->methods)];
- }
- else {
- mptr = vftbl->table[m->vftblindex];
- }
-
- /* and now get the codeinfo pointer from the first data segment slot */
-
- resm = code_get_methodinfo_for_pv(mptr);
-
- return resm;
-}
-
-
-/* method_get_parametercount **************************************************
-
- Use the descriptor of a method to determine the number of parameters
- of the method. The this pointer of non-static methods is not counted.
-
- IN:
- m........the method of which the parameters should be counted
-
- RETURN VALUE:
- The parameter count or -1 on error.
-
-*******************************************************************************/
-
-int32_t method_get_parametercount(methodinfo *m)
-{
- methoddesc *md; /* method descriptor of m */
- int32_t paramcount = 0; /* the parameter count of m */
-
- md = m->parseddesc;
-
- /* is the descriptor fully parsed? */
-
- if (md->params == NULL) {
- if (!descriptor_params_from_paramtypes(md, m->flags)) {
- return -1;
- }
- }
-
- paramcount = md->paramcount;
-
- /* skip `this' pointer */
-
- if (!(m->flags & ACC_STATIC)) {
- --paramcount;
- }
-
- return paramcount;
-}
-
-
-/* method_get_parametertypearray ***********************************************
-
- Use the descriptor of a method to generate a java.lang.Class array
- which contains the classes of the parametertypes of the method.
-
- This function is called by java.lang.reflect.{Constructor,Method}.
-
-*******************************************************************************/
-
-java_handle_objectarray_t *method_get_parametertypearray(methodinfo *m)
-{
- methoddesc *md;
- typedesc *paramtypes;
- int32_t paramcount;
- java_handle_objectarray_t *oa;
- int32_t i;
- classinfo *c;
-
- md = m->parseddesc;
-
- /* is the descriptor fully parsed? */
-
- if (m->parseddesc->params == NULL)
- if (!descriptor_params_from_paramtypes(md, m->flags))
- return NULL;
-
- paramtypes = md->paramtypes;
- paramcount = md->paramcount;
-
- /* skip `this' pointer */
-
- if (!(m->flags & ACC_STATIC)) {
- paramtypes++;
- paramcount--;
- }
-
- /* create class-array */
-
- oa = builtin_anewarray(paramcount, class_java_lang_Class);
-
- if (oa == NULL)
- return NULL;
-
- /* get classes */
-
- for (i = 0; i < paramcount; i++) {
- if (!resolve_class_from_typedesc(¶mtypes[i], true, false, &c))
- return NULL;
-
- LLNI_array_direct(oa, i) = (java_object_t *) c;
- }
-
- return oa;
-}
-
-
-/* method_get_exceptionarray ***************************************************
-
- Get the exceptions which can be thrown by a method.
-
-*******************************************************************************/
-
-java_handle_objectarray_t *method_get_exceptionarray(methodinfo *m)
-{
- java_handle_objectarray_t *oa;
- classinfo *c;
- s4 i;
-
- /* create class-array */
-
- oa = builtin_anewarray(m->thrownexceptionscount, class_java_lang_Class);
-
- if (oa == NULL)
- return NULL;
-
- /* iterate over all exceptions and store the class in the array */
-
- for (i = 0; i < m->thrownexceptionscount; i++) {
- c = resolve_classref_or_classinfo_eager(m->thrownexceptions[i], true);
-
- if (c == NULL)
- return NULL;
-
- LLNI_array_direct(oa, i) = (java_object_t *) c;
- }
-
- return oa;
-}
-
-
-/* method_returntype_get *******************************************************
-
- Get the return type of the method.
-
-*******************************************************************************/
-
-classinfo *method_returntype_get(methodinfo *m)
-{
- typedesc *td;
- classinfo *c;
-
- td = &(m->parseddesc->returntype);
-
- if (!resolve_class_from_typedesc(td, true, false, &c))
- return NULL;
-
- return c;
-}
-
-
-/* method_count_implementations ************************************************
-
- Count the implementations of a method in a class cone (a class and all its
- subclasses.)
-
- IN:
- m................the method to count
- c................class at which to start the counting (this class and
- all its subclasses will be searched)
-
- OUT:
- *found...........if found != NULL, *found receives the method
- implementation that was found. This value is only
- meaningful if the return value is 1.
-
- RETURN VALUE:
- the number of implementations found
-
-*******************************************************************************/
-
-s4 method_count_implementations(methodinfo *m, classinfo *c, methodinfo **found)
-{
- s4 count;
- methodinfo *mp;
- methodinfo *mend;
- classinfo *child;
-
- count = 0;
-
- mp = c->methods;
- mend = mp + c->methodscount;
-
- for (; mp < mend; ++mp) {
- if (method_canoverwrite(mp, m)) {
- if (found)
- *found = mp;
- count++;
- break;
- }
- }
-
- for (child = c->sub; child != NULL; child = child->nextsub) {
- count += method_count_implementations(m, child, found);
- }
-
- return count;
-}
-
-
-/* method_get_annotations ******************************************************
-
- Get a methods' unparsed annotations in a byte array.
-
- IN:
- m........the method of which the annotations should be returned
-
- RETURN VALUE:
- The unparsed annotations in a byte array (or NULL if there aren't any).
-
-*******************************************************************************/
-
-java_handle_bytearray_t *method_get_annotations(methodinfo *m)
-{
-#if defined(ENABLE_ANNOTATIONS)
- classinfo *c; /* methods' declaring class */
- int slot; /* methods' slot */
- java_handle_t *annotations; /* methods' unparsed annotations */
- java_handle_t *method_annotations; /* all methods' unparsed annotations */
- /* of the declaring class */
-
- c = m->clazz;
- slot = m - c->methods;
- annotations = NULL;
-
- LLNI_classinfo_field_get(c, method_annotations, method_annotations);
-
- /* the method_annotations array might be shorter then the method
- * count if the methods above a certain index have no annotations.
- */
- if (method_annotations != NULL &&
- array_length_get(method_annotations) > slot) {
- annotations = array_objectarray_element_get(
- (java_handle_objectarray_t*)method_annotations, slot);
- }
-
- return (java_handle_bytearray_t*)annotations;
-#else
- return NULL;
-#endif
-}
-
-
-/* method_get_parameterannotations ********************************************
-
- Get a methods' unparsed parameter annotations in an array of byte
- arrays.
-
- IN:
- m........the method of which the parameter annotations should be
- returned
-
- RETURN VALUE:
- The unparsed parameter annotations in a byte array (or NULL if
- there aren't any).
-
-*******************************************************************************/
-
-java_handle_bytearray_t *method_get_parameterannotations(methodinfo *m)
-{
-#if defined(ENABLE_ANNOTATIONS)
- classinfo *c; /* methods' declaring class */
- int slot; /* methods' slot */
- java_handle_t *parameterAnnotations; /* methods' unparsed */
- /* parameter annotations */
- java_handle_t *method_parameterannotations; /* all methods' unparsed */
- /* parameter annotations of */
- /* the declaring class */
-
- c = m->clazz;
- slot = m - c->methods;
- parameterAnnotations = NULL;
-
- LLNI_classinfo_field_get(
- c, method_parameterannotations, method_parameterannotations);
-
- /* the method_annotations array might be shorter then the method
- * count if the methods above a certain index have no annotations.
- */
- if (method_parameterannotations != NULL &&
- array_length_get(method_parameterannotations) > slot) {
- parameterAnnotations = array_objectarray_element_get(
- (java_handle_objectarray_t*)method_parameterannotations,
- slot);
- }
-
- return (java_handle_bytearray_t*)parameterAnnotations;
-#else
- return NULL;
-#endif
-}
-
-
-/* method_get_annotationdefault ***********************************************
-
- Get a methods' unparsed annotation default value in a byte array.
-
- IN:
- m........the method of which the annotation default value should be
- returned
-
- RETURN VALUE:
- The unparsed annotation default value in a byte array (or NULL if
- there isn't one).
-
-*******************************************************************************/
-
-java_handle_bytearray_t *method_get_annotationdefault(methodinfo *m)
-{
-#if defined(ENABLE_ANNOTATIONS)
- classinfo *c; /* methods' declaring class */
- int slot; /* methods' slot */
- java_handle_t *annotationDefault; /* methods' unparsed */
- /* annotation default value */
- java_handle_t *method_annotationdefaults; /* all methods' unparsed */
- /* annotation default values of */
- /* the declaring class */
-
- c = m->clazz;
- slot = m - c->methods;
- annotationDefault = NULL;
-
- LLNI_classinfo_field_get(
- c, method_annotationdefaults, method_annotationdefaults);
-
- /* the method_annotations array might be shorter then the method
- * count if the methods above a certain index have no annotations.
- */
- if (method_annotationdefaults != NULL &&
- array_length_get(method_annotationdefaults) > slot) {
- annotationDefault = array_objectarray_element_get(
- (java_handle_objectarray_t*)method_annotationdefaults, slot);
- }
-
- return (java_handle_bytearray_t*)annotationDefault;
-#else
- return NULL;
-#endif
-}
-
-
-/* method_add_to_worklist ******************************************************
-
- Add the method to the given worklist. If the method already occurs in
- the worklist, the worklist remains unchanged.
-
-*******************************************************************************/
-
-static void method_add_to_worklist(methodinfo *m, method_worklist **wl)
-{
- method_worklist *wi;
-
- for (wi = *wl; wi != NULL; wi = wi->next)
- if (wi->m == m)
- return;
-
- wi = NEW(method_worklist);
- wi->next = *wl;
- wi->m = m;
-
- *wl = wi;
-}
-
-
-/* method_add_assumption_monomorphic *******************************************
-
- Record the assumption that the method is monomorphic.
-
- IN:
- m.................the method
- caller............the caller making the assumption
-
-*******************************************************************************/
-
-void method_add_assumption_monomorphic(methodinfo *m, methodinfo *caller)
-{
- method_assumption *as;
-
- /* XXX LOCKING FOR THIS FUNCTION? */
-
- /* check if we already have registered this assumption */
-
- for (as = m->assumptions; as != NULL; as = as->next) {
- if (as->context == caller)
- return;
- }
-
- /* register the assumption */
-
- as = NEW(method_assumption);
- as->next = m->assumptions;
- as->context = caller;
-
- m->assumptions = as;
-}
-
-/* method_break_assumption_monomorphic *****************************************
-
- Break the assumption that this method is monomorphic. All callers that
- have registered this assumption are added to the worklist.
-
- IN:
- m.................the method
- wl................worklist where to add invalidated callers
-
-*******************************************************************************/
-
-void method_break_assumption_monomorphic(methodinfo *m, method_worklist **wl)
-{
- method_assumption *as;
-
- /* XXX LOCKING FOR THIS FUNCTION? */
-
- for (as = m->assumptions; as != NULL; as = as->next) {
- INLINELOG(
- printf("ASSUMPTION BROKEN (monomorphism): ");
- method_print(m);
- printf(" in ");
- method_println(as->context);
- );
-
- method_add_to_worklist(as->context, wl);
-
-#if defined(ENABLE_TLH) && 0
- /* XXX hack */
- method_assumption *as2;
- as2 = m->assumptions;
- m->assumptions = NULL;
- method_break_assumption_monomorphic(as->context, wl);
- /*
- assert(m->assumptions == NULL);
- m->assumptions = as2;*/
-#endif
-
- }
-}
-
-/* method_printflags ***********************************************************
-
- Prints the flags of a method to stdout like.
-
-*******************************************************************************/
-
-#if !defined(NDEBUG)
-void method_printflags(methodinfo *m)
-{
- if (m == NULL) {
- printf("NULL");
- return;
- }
-
- if (m->flags & ACC_PUBLIC) printf(" PUBLIC");
- if (m->flags & ACC_PRIVATE) printf(" PRIVATE");
- if (m->flags & ACC_PROTECTED) printf(" PROTECTED");
- if (m->flags & ACC_STATIC) printf(" STATIC");
- if (m->flags & ACC_FINAL) printf(" FINAL");
- if (m->flags & ACC_SYNCHRONIZED) printf(" SYNCHRONIZED");
- if (m->flags & ACC_VOLATILE) printf(" VOLATILE");
- if (m->flags & ACC_TRANSIENT) printf(" TRANSIENT");
- if (m->flags & ACC_NATIVE) printf(" NATIVE");
- if (m->flags & ACC_INTERFACE) printf(" INTERFACE");
- if (m->flags & ACC_ABSTRACT) printf(" ABSTRACT");
- if (m->flags & ACC_METHOD_BUILTIN) printf(" (builtin)");
- if (m->flags & ACC_METHOD_MONOMORPHIC) printf(" (mono)");
- if (m->flags & ACC_METHOD_IMPLEMENTED) printf(" (impl)");
-}
-#endif /* !defined(NDEBUG) */
-
-
-/* method_print ****************************************************************
-
- Prints a method to stdout like:
-
- java.lang.Object.<init>()V
-
-*******************************************************************************/
-
-#if !defined(NDEBUG)
-void method_print(methodinfo *m)
-{
- if (m == NULL) {
- printf("NULL");
- return;
- }
-
- if (m->clazz != NULL)
- utf_display_printable_ascii_classname(m->clazz->name);
- else
- printf("NULL");
- printf(".");
- utf_display_printable_ascii(m->name);
- utf_display_printable_ascii(m->descriptor);
-
- method_printflags(m);
-}
-#endif /* !defined(NDEBUG) */
-
-
-/* method_println **************************************************************
-
- Prints a method plus new line to stdout like:
-
- java.lang.Object.<init>()V
-
-*******************************************************************************/
-
-#if !defined(NDEBUG)
-void method_println(methodinfo *m)
-{
- if (opt_debugcolor) printf("\033[31m"); /* red */
- method_print(m);
- if (opt_debugcolor) printf("\033[m");
- printf("\n");
-}
-#endif /* !defined(NDEBUG) */
-
-
-/* method_methodref_print ******************************************************
-
- Prints a method reference to stdout.
-
-*******************************************************************************/
-
-#if !defined(NDEBUG)
-void method_methodref_print(constant_FMIref *mr)
-{
- if (!mr) {
- printf("(constant_FMIref *)NULL");
- return;
- }
-
- if (IS_FMIREF_RESOLVED(mr)) {
- printf("<method> ");
- method_print(mr->p.method);
- }
- else {
- printf("<methodref> ");
- utf_display_printable_ascii_classname(mr->p.classref->name);
- printf(".");
- utf_display_printable_ascii(mr->name);
- utf_display_printable_ascii(mr->descriptor);
- }
-}
-#endif /* !defined(NDEBUG) */
-
-
-/* method_methodref_println ****************************************************
-
- Prints a method reference to stdout, followed by a newline.
-
-*******************************************************************************/
-
-#if !defined(NDEBUG)
-void method_methodref_println(constant_FMIref *mr)
-{
- method_methodref_print(mr);
- printf("\n");
-}
-#endif /* !defined(NDEBUG) */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/method.cpp - method functions
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "vm/types.h"
+
+#include "mm/memory.h"
+
+#include "native/llni.h"
+
+#include "threads/mutex.hpp"
+
+#include "vm/array.hpp"
+#include "vm/jit/builtin.hpp"
+#include "vm/class.hpp"
+#include "vm/exceptions.hpp"
+#include "vm/global.h"
+#include "vm/globals.hpp"
+#include "vm/linker.hpp"
+#include "vm/loader.hpp"
+#include "vm/method.hpp"
+#include "vm/options.h"
+#include "vm/resolve.hpp"
+#include "vm/suck.hpp"
+#include "vm/utf8.h"
+#include "vm/vm.hpp"
+
+#include "vm/jit/code.hpp"
+#include "vm/jit/methodheader.h"
+#include "vm/jit/stubs.hpp"
+
+
+#if !defined(NDEBUG) && defined(ENABLE_INLINING)
+#define INLINELOG(code) do { if (opt_TraceInlining) { code } } while (0)
+#else
+#define INLINELOG(code)
+#endif
+
+/* global variables ***********************************************************/
+
+methodinfo *method_java_lang_reflect_Method_invoke;
+
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+/* method_init *****************************************************************
+
+ Initialize method subsystem.
+
+*******************************************************************************/
+
+void method_init(void)
+{
+#if defined(ENABLE_JAVASE)
+ /* Sanity check. */
+
+ if (class_java_lang_reflect_Method == NULL)
+ vm_abort("method_init: class_java_lang_reflect_Method is NULL");
+
+ /* Cache java.lang.reflect.Method.invoke() */
+
+ method_java_lang_reflect_Method_invoke =
+ class_findmethod(class_java_lang_reflect_Method, utf_invoke, NULL);
+
+ if (method_java_lang_reflect_Method_invoke == NULL)
+ vm_abort("method_init: Could not resolve method java.lang.reflect.Method.invoke().");
+#endif
+}
+
+
+/* method_load *****************************************************************
+
+ Loads a method from the class file and fills an existing methodinfo
+ structure.
+
+ method_info {
+ u2 access_flags;
+ u2 name_index;
+ u2 descriptor_index;
+ u2 attributes_count;
+ attribute_info attributes[attribute_count];
+ }
+
+ attribute_info {
+ u2 attribute_name_index;
+ u4 attribute_length;
+ u1 info[attribute_length];
+ }
+
+ LineNumberTable_attribute {
+ u2 attribute_name_index;
+ u4 attribute_length;
+ u2 line_number_table_length;
+ {
+ u2 start_pc;
+ u2 line_number;
+ } line_number_table[line_number_table_length];
+ }
+
+*******************************************************************************/
+
+bool method_load(classbuffer *cb, methodinfo *m, descriptor_pool *descpool)
+{
+ classinfo *c;
+ int argcount;
+ s4 i, j, k, l;
+ utf *u;
+ u2 name_index;
+ u2 descriptor_index;
+ u2 attributes_count;
+ u2 attribute_name_index;
+ utf *attribute_name;
+ u2 code_attributes_count;
+ u2 code_attribute_name_index;
+ utf *code_attribute_name;
+
+ /* get classinfo */
+
+ c = cb->clazz;
+
+ m->mutex = new Mutex();
+
+#if defined(ENABLE_STATISTICS)
+ if (opt_stat)
+ count_all_methods++;
+#endif
+
+ /* all fields of m have been zeroed in load_class_from_classbuffer */
+
+ m->clazz = c;
+
+ if (!suck_check_classbuffer_size(cb, 2 + 2 + 2))
+ return false;
+
+ /* access flags */
+
+ m->flags = suck_u2(cb);
+
+ /* name */
+
+ name_index = suck_u2(cb);
+
+ if (!(u = (utf*) class_getconstant(c, name_index, CONSTANT_Utf8)))
+ return false;
+
+ m->name = u;
+
+ /* descriptor */
+
+ descriptor_index = suck_u2(cb);
+
+ if (!(u = (utf*) class_getconstant(c, descriptor_index, CONSTANT_Utf8)))
+ return false;
+
+ m->descriptor = u;
+
+ if (!descriptor_pool_add(descpool, u, &argcount))
+ return false;
+
+#ifdef ENABLE_VERIFIER
+ if (opt_verify) {
+ if (!is_valid_name_utf(m->name)) {
+ exceptions_throw_classformaterror(c, "Method with invalid name");
+ return false;
+ }
+
+ if (m->name->text[0] == '<' &&
+ m->name != utf_init && m->name != utf_clinit) {
+ exceptions_throw_classformaterror(c, "Method with invalid special name");
+ return false;
+ }
+ }
+#endif /* ENABLE_VERIFIER */
+
+ if (!(m->flags & ACC_STATIC))
+ argcount++; /* count the 'this' argument */
+
+#ifdef ENABLE_VERIFIER
+ if (opt_verify) {
+ if (argcount > 255) {
+ exceptions_throw_classformaterror(c, "Too many arguments in signature");
+ return false;
+ }
+
+ /* check flag consistency */
+ if (m->name != utf_clinit) {
+ i = (m->flags & (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED));
+
+ if (i != 0 && i != ACC_PUBLIC && i != ACC_PRIVATE && i != ACC_PROTECTED) {
+ exceptions_throw_classformaterror(c,
+ "Illegal method modifiers: 0x%X",
+ m->flags);
+ return false;
+ }
+
+ if (m->flags & ACC_ABSTRACT) {
+ if ((m->flags & (ACC_FINAL | ACC_NATIVE | ACC_PRIVATE |
+ ACC_STATIC | ACC_STRICT | ACC_SYNCHRONIZED))) {
+ exceptions_throw_classformaterror(c,
+ "Illegal method modifiers: 0x%X",
+ m->flags);
+ return false;
+ }
+ }
+
+ if (c->flags & ACC_INTERFACE) {
+ if ((m->flags & (ACC_ABSTRACT | ACC_PUBLIC)) != (ACC_ABSTRACT | ACC_PUBLIC)) {
+ exceptions_throw_classformaterror(c,
+ "Illegal method modifiers: 0x%X",
+ m->flags);
+ return false;
+ }
+ }
+
+ if (m->name == utf_init) {
+ if (m->flags & (ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED |
+ ACC_NATIVE | ACC_ABSTRACT)) {
+ exceptions_throw_classformaterror(c, "Instance initialization method has invalid flags set");
+ return false;
+ }
+ }
+ }
+ }
+#endif /* ENABLE_VERIFIER */
+
+ /* mark the method as monomorphic until further notice */
+
+ m->flags |= ACC_METHOD_MONOMORPHIC;
+
+ /* non-abstract methods have an implementation in this class */
+
+ if (!(m->flags & ACC_ABSTRACT))
+ m->flags |= ACC_METHOD_IMPLEMENTED;
+
+ if (!suck_check_classbuffer_size(cb, 2))
+ return false;
+
+ /* attributes count */
+
+ attributes_count = suck_u2(cb);
+
+ for (i = 0; i < attributes_count; i++) {
+ if (!suck_check_classbuffer_size(cb, 2))
+ return false;
+
+ /* attribute name index */
+
+ attribute_name_index = suck_u2(cb);
+
+ attribute_name =
+ (utf*) class_getconstant(c, attribute_name_index, CONSTANT_Utf8);
+
+ if (attribute_name == NULL)
+ return false;
+
+ if (attribute_name == utf_Code) {
+ /* Code */
+
+ if (m->flags & (ACC_ABSTRACT | ACC_NATIVE)) {
+ exceptions_throw_classformaterror(c, "Code attribute in native or abstract methods");
+ return false;
+ }
+
+ if (m->jcode) {
+ exceptions_throw_classformaterror(c, "Multiple Code attributes");
+ return false;
+ }
+
+ if (!suck_check_classbuffer_size(cb, 4 + 2 + 2))
+ return false;
+
+ suck_u4(cb);
+ m->maxstack = suck_u2(cb);
+ m->maxlocals = suck_u2(cb);
+
+ if (m->maxlocals < argcount) {
+ exceptions_throw_classformaterror(c, "Arguments can't fit into locals");
+ return false;
+ }
+
+ if (!suck_check_classbuffer_size(cb, 4))
+ return false;
+
+ m->jcodelength = suck_u4(cb);
+
+ if (m->jcodelength == 0) {
+ exceptions_throw_classformaterror(c, "Code of a method has length 0");
+ return false;
+ }
+
+ if (m->jcodelength > 65535) {
+ exceptions_throw_classformaterror(c, "Code of a method longer than 65535 bytes");
+ return false;
+ }
+
+ if (!suck_check_classbuffer_size(cb, m->jcodelength))
+ return false;
+
+ m->jcode = MNEW(u1, m->jcodelength);
+ suck_nbytes(m->jcode, cb, m->jcodelength);
+
+ if (!suck_check_classbuffer_size(cb, 2))
+ return false;
+
+ m->rawexceptiontablelength = suck_u2(cb);
+ if (!suck_check_classbuffer_size(cb, (2 + 2 + 2 + 2) * m->rawexceptiontablelength))
+ return false;
+
+ m->rawexceptiontable = MNEW(raw_exception_entry, m->rawexceptiontablelength);
+
+#if defined(ENABLE_STATISTICS)
+ if (opt_stat) {
+ count_vmcode_len += m->jcodelength + 18;
+ count_extable_len +=
+ m->rawexceptiontablelength * sizeof(raw_exception_entry);
+ }
+#endif
+
+ for (j = 0; j < m->rawexceptiontablelength; j++) {
+ u4 idx;
+ m->rawexceptiontable[j].startpc = suck_u2(cb);
+ m->rawexceptiontable[j].endpc = suck_u2(cb);
+ m->rawexceptiontable[j].handlerpc = suck_u2(cb);
+
+ idx = suck_u2(cb);
+
+ if (!idx) {
+ m->rawexceptiontable[j].catchtype.any = NULL;
+ }
+ else {
+ /* the classref is created later */
+ if (!(m->rawexceptiontable[j].catchtype.any =
+ (utf *) class_getconstant(c, idx, CONSTANT_Class)))
+ return false;
+ }
+ }
+
+ if (!suck_check_classbuffer_size(cb, 2))
+ return false;
+
+ /* code attributes count */
+
+ code_attributes_count = suck_u2(cb);
+
+ for (k = 0; k < code_attributes_count; k++) {
+ if (!suck_check_classbuffer_size(cb, 2))
+ return false;
+
+ /* code attribute name index */
+
+ code_attribute_name_index = suck_u2(cb);
+
+ code_attribute_name =
+ (utf*) class_getconstant(c, code_attribute_name_index, CONSTANT_Utf8);
+
+ if (code_attribute_name == NULL)
+ return false;
+
+ /* check which code attribute */
+
+ if (code_attribute_name == utf_LineNumberTable) {
+ /* LineNumberTable */
+
+ if (!suck_check_classbuffer_size(cb, 4 + 2))
+ return false;
+
+ /* attribute length */
+
+ (void) suck_u4(cb);
+
+ /* line number table length */
+
+ m->linenumbercount = suck_u2(cb);
+
+ if (!suck_check_classbuffer_size(cb,
+ (2 + 2) * m->linenumbercount))
+ return false;
+
+ m->linenumbers = MNEW(lineinfo, m->linenumbercount);
+
+#if defined(ENABLE_STATISTICS)
+ if (opt_stat)
+ size_lineinfo += sizeof(lineinfo) * m->linenumbercount;
+#endif
+
+ for (l = 0; l < m->linenumbercount; l++) {
+ m->linenumbers[l].start_pc = suck_u2(cb);
+ m->linenumbers[l].line_number = suck_u2(cb);
+ }
+ }
+#if defined(ENABLE_JAVASE)
+ else if (code_attribute_name == utf_StackMapTable) {
+ /* StackTableMap */
+
+ if (!stackmap_load_attribute_stackmaptable(cb, m))
+ return false;
+ }
+#endif
+ else {
+ /* unknown code attribute */
+
+ if (!loader_skip_attribute_body(cb))
+ return false;
+ }
+ }
+ }
+ else if (attribute_name == utf_Exceptions) {
+ /* Exceptions */
+
+ if (m->thrownexceptions != NULL) {
+ exceptions_throw_classformaterror(c, "Multiple Exceptions attributes");
+ return false;
+ }
+
+ if (!suck_check_classbuffer_size(cb, 4 + 2))
+ return false;
+
+ /* attribute length */
+
+ (void) suck_u4(cb);
+
+ m->thrownexceptionscount = suck_u2(cb);
+
+ if (!suck_check_classbuffer_size(cb, 2 * m->thrownexceptionscount))
+ return false;
+
+ m->thrownexceptions = MNEW(classref_or_classinfo, m->thrownexceptionscount);
+
+ for (j = 0; j < m->thrownexceptionscount; j++) {
+ /* the classref is created later */
+ if (!((m->thrownexceptions)[j].any =
+ (utf*) class_getconstant(c, suck_u2(cb), CONSTANT_Class)))
+ return false;
+ }
+ }
+#if defined(ENABLE_JAVASE)
+ else if (attribute_name == utf_Signature) {
+ /* Signature */
+
+ if (!loader_load_attribute_signature(cb, &(m->signature)))
+ return false;
+ }
+
+#if defined(ENABLE_ANNOTATIONS)
+ else if (attribute_name == utf_RuntimeVisibleAnnotations) {
+ /* RuntimeVisibleAnnotations */
+ if (!annotation_load_method_attribute_runtimevisibleannotations(cb, m))
+ return false;
+ }
+ else if (attribute_name == utf_RuntimeInvisibleAnnotations) {
+ /* RuntimeInvisibleAnnotations */
+ if (!annotation_load_method_attribute_runtimeinvisibleannotations(cb, m))
+ return false;
+ }
+ else if (attribute_name == utf_RuntimeVisibleParameterAnnotations) {
+ /* RuntimeVisibleParameterAnnotations */
+ if (!annotation_load_method_attribute_runtimevisibleparameterannotations(cb, m))
+ return false;
+ }
+ else if (attribute_name == utf_RuntimeInvisibleParameterAnnotations) {
+ /* RuntimeInvisibleParameterAnnotations */
+ if (!annotation_load_method_attribute_runtimeinvisibleparameterannotations(cb, m))
+ return false;
+ }
+ else if (attribute_name == utf_AnnotationDefault) {
+ /* AnnotationDefault */
+ if (!annotation_load_method_attribute_annotationdefault(cb, m))
+ return false;
+ }
+#endif
+#endif
+ else {
+ /* unknown attribute */
+
+ if (!loader_skip_attribute_body(cb))
+ return false;
+ }
+ }
+
+ if ((m->jcode == NULL) && !(m->flags & (ACC_ABSTRACT | ACC_NATIVE))) {
+ exceptions_throw_classformaterror(c, "Missing Code attribute");
+ return false;
+ }
+
+#if defined(ENABLE_REPLACEMENT)
+ /* initialize the hit countdown field */
+
+ m->hitcountdown = METHOD_INITIAL_HIT_COUNTDOWN;
+#endif
+
+ /* everything was ok */
+
+ return true;
+}
+
+
+/* method_free *****************************************************************
+
+ Frees all memory that was allocated for this method.
+
+*******************************************************************************/
+
+void method_free(methodinfo *m)
+{
+ if (m->mutex)
+ delete m->mutex;
+
+ if (m->jcode)
+ MFREE(m->jcode, u1, m->jcodelength);
+
+ if (m->rawexceptiontable)
+ MFREE(m->rawexceptiontable, raw_exception_entry, m->rawexceptiontablelength);
+
+ code_free_code_of_method(m);
+
+ if (m->stubroutine) {
+ if (m->flags & ACC_NATIVE) {
+ NativeStub::remove(m->stubroutine);
+ }
+ else {
+ CompilerStub::remove(m->stubroutine);
+ }
+ }
+}
+
+
+/* method_canoverwrite *********************************************************
+
+ Check if m and old are identical with respect to type and
+ name. This means that old can be overwritten with m.
+
+*******************************************************************************/
+
+bool method_canoverwrite(methodinfo *m, methodinfo *old)
+{
+ if (m->name != old->name)
+ return false;
+
+ if (m->descriptor != old->descriptor)
+ return false;
+
+ if (m->flags & ACC_STATIC)
+ return false;
+
+ return true;
+}
+
+
+/* method_new_builtin **********************************************************
+
+ Creates a minimal methodinfo structure for builtins. This comes handy
+ when dealing with builtin stubs or stacktraces.
+
+*******************************************************************************/
+
+methodinfo *method_new_builtin(builtintable_entry *bte)
+{
+ methodinfo *m;
+
+ /* allocate the methodinfo structure */
+
+ m = NEW(methodinfo);
+
+ /* initialize methodinfo structure */
+
+ MZERO(m, methodinfo, 1);
+
+ m->mutex = new Mutex();
+ m->flags = ACC_METHOD_BUILTIN;
+ m->parseddesc = bte->md;
+ m->name = bte->name;
+ m->descriptor = bte->descriptor;
+
+ /* return the newly created methodinfo */
+
+ return m;
+}
+
+
+/* method_vftbl_lookup *********************************************************
+
+ Does a method lookup in the passed virtual function table. This
+ function does exactly the same thing as JIT, but additionally
+ relies on the fact, that the methodinfo pointer is at the first
+ data segment slot (even for compiler stubs).
+
+*******************************************************************************/
+
+methodinfo *method_vftbl_lookup(vftbl_t *vftbl, methodinfo* m)
+{
+ methodptr mptr;
+ methodptr *pmptr;
+ methodinfo *resm; /* pointer to new resolved method */
+
+ /* If the method is not an instance method, just return it. */
+
+ if (m->flags & ACC_STATIC)
+ return m;
+
+ assert(vftbl);
+
+ /* Get the method from the virtual function table. Is this an
+ interface method? */
+
+ if (m->clazz->flags & ACC_INTERFACE) {
+ pmptr = vftbl->interfacetable[-(m->clazz->index)];
+ mptr = pmptr[(m - m->clazz->methods)];
+ }
+ else {
+ mptr = vftbl->table[m->vftblindex];
+ }
+
+ /* and now get the codeinfo pointer from the first data segment slot */
+
+ resm = code_get_methodinfo_for_pv(mptr);
+
+ return resm;
+}
+
+
+/* method_get_parametercount **************************************************
+
+ Use the descriptor of a method to determine the number of parameters
+ of the method. The this pointer of non-static methods is not counted.
+
+ IN:
+ m........the method of which the parameters should be counted
+
+ RETURN VALUE:
+ The parameter count or -1 on error.
+
+*******************************************************************************/
+
+int32_t method_get_parametercount(methodinfo *m)
+{
+ methoddesc *md; /* method descriptor of m */
+ int32_t paramcount = 0; /* the parameter count of m */
+
+ md = m->parseddesc;
+
+ /* is the descriptor fully parsed? */
+
+ if (md->params == NULL) {
+ if (!descriptor_params_from_paramtypes(md, m->flags)) {
+ return -1;
+ }
+ }
+
+ paramcount = md->paramcount;
+
+ /* skip `this' pointer */
+
+ if (!(m->flags & ACC_STATIC)) {
+ --paramcount;
+ }
+
+ return paramcount;
+}
+
+
+/* method_get_parametertypearray ***********************************************
+
+ Use the descriptor of a method to generate a java.lang.Class array
+ which contains the classes of the parametertypes of the method.
+
+ This function is called by java.lang.reflect.{Constructor,Method}.
+
+*******************************************************************************/
+
+java_handle_objectarray_t *method_get_parametertypearray(methodinfo *m)
+{
+ methoddesc *md;
+ typedesc *paramtypes;
+ int32_t paramcount;
+ java_handle_objectarray_t *oa;
+ int32_t i;
+ classinfo *c;
+
+ md = m->parseddesc;
+
+ /* is the descriptor fully parsed? */
+
+ if (m->parseddesc->params == NULL)
+ if (!descriptor_params_from_paramtypes(md, m->flags))
+ return NULL;
+
+ paramtypes = md->paramtypes;
+ paramcount = md->paramcount;
+
+ /* skip `this' pointer */
+
+ if (!(m->flags & ACC_STATIC)) {
+ paramtypes++;
+ paramcount--;
+ }
+
+ /* create class-array */
+
+ oa = builtin_anewarray(paramcount, class_java_lang_Class);
+
+ if (oa == NULL)
+ return NULL;
+
+ /* get classes */
+
+ for (i = 0; i < paramcount; i++) {
+ if (!resolve_class_from_typedesc(¶mtypes[i], true, false, &c))
+ return NULL;
+
+ LLNI_array_direct(oa, i) = (java_object_t *) c;
+ }
+
+ return oa;
+}
+
+
+/* method_get_exceptionarray ***************************************************
+
+ Get the exceptions which can be thrown by a method.
+
+*******************************************************************************/
+
+java_handle_objectarray_t *method_get_exceptionarray(methodinfo *m)
+{
+ java_handle_objectarray_t *oa;
+ classinfo *c;
+ s4 i;
+
+ /* create class-array */
+
+ oa = builtin_anewarray(m->thrownexceptionscount, class_java_lang_Class);
+
+ if (oa == NULL)
+ return NULL;
+
+ /* iterate over all exceptions and store the class in the array */
+
+ for (i = 0; i < m->thrownexceptionscount; i++) {
+ c = resolve_classref_or_classinfo_eager(m->thrownexceptions[i], true);
+
+ if (c == NULL)
+ return NULL;
+
+ LLNI_array_direct(oa, i) = (java_object_t *) c;
+ }
+
+ return oa;
+}
+
+
+/* method_returntype_get *******************************************************
+
+ Get the return type of the method.
+
+*******************************************************************************/
+
+classinfo *method_returntype_get(methodinfo *m)
+{
+ typedesc *td;
+ classinfo *c;
+
+ td = &(m->parseddesc->returntype);
+
+ if (!resolve_class_from_typedesc(td, true, false, &c))
+ return NULL;
+
+ return c;
+}
+
+
+/* method_count_implementations ************************************************
+
+ Count the implementations of a method in a class cone (a class and all its
+ subclasses.)
+
+ IN:
+ m................the method to count
+ c................class at which to start the counting (this class and
+ all its subclasses will be searched)
+
+ OUT:
+ *found...........if found != NULL, *found receives the method
+ implementation that was found. This value is only
+ meaningful if the return value is 1.
+
+ RETURN VALUE:
+ the number of implementations found
+
+*******************************************************************************/
+
+s4 method_count_implementations(methodinfo *m, classinfo *c, methodinfo **found)
+{
+ s4 count;
+ methodinfo *mp;
+ methodinfo *mend;
+ classinfo *child;
+
+ count = 0;
+
+ mp = c->methods;
+ mend = mp + c->methodscount;
+
+ for (; mp < mend; ++mp) {
+ if (method_canoverwrite(mp, m)) {
+ if (found)
+ *found = mp;
+ count++;
+ break;
+ }
+ }
+
+ for (child = c->sub; child != NULL; child = child->nextsub) {
+ count += method_count_implementations(m, child, found);
+ }
+
+ return count;
+}
+
+
+/* method_get_annotations ******************************************************
+
+ Get a methods' unparsed annotations in a byte array.
+
+ IN:
+ m........the method of which the annotations should be returned
+
+ RETURN VALUE:
+ The unparsed annotations in a byte array (or NULL if there aren't any).
+
+*******************************************************************************/
+
+java_handle_bytearray_t *method_get_annotations(methodinfo *m)
+{
+#if defined(ENABLE_ANNOTATIONS)
+ classinfo *c; /* methods' declaring class */
+ int slot; /* methods' slot */
+ java_handle_t *annotations; /* methods' unparsed annotations */
+ java_handle_t *method_annotations; /* all methods' unparsed annotations */
+ /* of the declaring class */
+
+ c = m->clazz;
+ slot = m - c->methods;
+ annotations = NULL;
+
+ LLNI_classinfo_field_get(c, method_annotations, method_annotations);
+
+ /* the method_annotations array might be shorter then the method
+ * count if the methods above a certain index have no annotations.
+ */
+ if (method_annotations != NULL &&
+ array_length_get(method_annotations) > slot) {
+ annotations = array_objectarray_element_get(
+ (java_handle_objectarray_t*)method_annotations, slot);
+ }
+
+ return (java_handle_bytearray_t*)annotations;
+#else
+ return NULL;
+#endif
+}
+
+
+/* method_get_parameterannotations ********************************************
+
+ Get a methods' unparsed parameter annotations in an array of byte
+ arrays.
+
+ IN:
+ m........the method of which the parameter annotations should be
+ returned
+
+ RETURN VALUE:
+ The unparsed parameter annotations in a byte array (or NULL if
+ there aren't any).
+
+*******************************************************************************/
+
+java_handle_bytearray_t *method_get_parameterannotations(methodinfo *m)
+{
+#if defined(ENABLE_ANNOTATIONS)
+ classinfo *c; /* methods' declaring class */
+ int slot; /* methods' slot */
+ java_handle_t *parameterAnnotations; /* methods' unparsed */
+ /* parameter annotations */
+ java_handle_t *method_parameterannotations; /* all methods' unparsed */
+ /* parameter annotations of */
+ /* the declaring class */
+
+ c = m->clazz;
+ slot = m - c->methods;
+ parameterAnnotations = NULL;
+
+ LLNI_classinfo_field_get(
+ c, method_parameterannotations, method_parameterannotations);
+
+ /* the method_annotations array might be shorter then the method
+ * count if the methods above a certain index have no annotations.
+ */
+ if (method_parameterannotations != NULL &&
+ array_length_get(method_parameterannotations) > slot) {
+ parameterAnnotations = array_objectarray_element_get(
+ (java_handle_objectarray_t*)method_parameterannotations,
+ slot);
+ }
+
+ return (java_handle_bytearray_t*)parameterAnnotations;
+#else
+ return NULL;
+#endif
+}
+
+
+/* method_get_annotationdefault ***********************************************
+
+ Get a methods' unparsed annotation default value in a byte array.
+
+ IN:
+ m........the method of which the annotation default value should be
+ returned
+
+ RETURN VALUE:
+ The unparsed annotation default value in a byte array (or NULL if
+ there isn't one).
+
+*******************************************************************************/
+
+java_handle_bytearray_t *method_get_annotationdefault(methodinfo *m)
+{
+#if defined(ENABLE_ANNOTATIONS)
+ classinfo *c; /* methods' declaring class */
+ int slot; /* methods' slot */
+ java_handle_t *annotationDefault; /* methods' unparsed */
+ /* annotation default value */
+ java_handle_t *method_annotationdefaults; /* all methods' unparsed */
+ /* annotation default values of */
+ /* the declaring class */
+
+ c = m->clazz;
+ slot = m - c->methods;
+ annotationDefault = NULL;
+
+ LLNI_classinfo_field_get(
+ c, method_annotationdefaults, method_annotationdefaults);
+
+ /* the method_annotations array might be shorter then the method
+ * count if the methods above a certain index have no annotations.
+ */
+ if (method_annotationdefaults != NULL &&
+ array_length_get(method_annotationdefaults) > slot) {
+ annotationDefault = array_objectarray_element_get(
+ (java_handle_objectarray_t*)method_annotationdefaults, slot);
+ }
+
+ return (java_handle_bytearray_t*)annotationDefault;
+#else
+ return NULL;
+#endif
+}
+
+
+/* method_add_to_worklist ******************************************************
+
+ Add the method to the given worklist. If the method already occurs in
+ the worklist, the worklist remains unchanged.
+
+*******************************************************************************/
+
+static void method_add_to_worklist(methodinfo *m, method_worklist **wl)
+{
+ method_worklist *wi;
+
+ for (wi = *wl; wi != NULL; wi = wi->next)
+ if (wi->m == m)
+ return;
+
+ wi = NEW(method_worklist);
+ wi->next = *wl;
+ wi->m = m;
+
+ *wl = wi;
+}
+
+
+/* method_add_assumption_monomorphic *******************************************
+
+ Record the assumption that the method is monomorphic.
+
+ IN:
+ m.................the method
+ caller............the caller making the assumption
+
+*******************************************************************************/
+
+void method_add_assumption_monomorphic(methodinfo *m, methodinfo *caller)
+{
+ method_assumption *as;
+
+ /* XXX LOCKING FOR THIS FUNCTION? */
+
+ /* check if we already have registered this assumption */
+
+ for (as = m->assumptions; as != NULL; as = as->next) {
+ if (as->context == caller)
+ return;
+ }
+
+ /* register the assumption */
+
+ as = NEW(method_assumption);
+ as->next = m->assumptions;
+ as->context = caller;
+
+ m->assumptions = as;
+}
+
+/* method_break_assumption_monomorphic *****************************************
+
+ Break the assumption that this method is monomorphic. All callers that
+ have registered this assumption are added to the worklist.
+
+ IN:
+ m.................the method
+ wl................worklist where to add invalidated callers
+
+*******************************************************************************/
+
+void method_break_assumption_monomorphic(methodinfo *m, method_worklist **wl)
+{
+ method_assumption *as;
+
+ /* XXX LOCKING FOR THIS FUNCTION? */
+
+ for (as = m->assumptions; as != NULL; as = as->next) {
+ INLINELOG(
+ printf("ASSUMPTION BROKEN (monomorphism): ");
+ method_print(m);
+ printf(" in ");
+ method_println(as->context);
+ );
+
+ method_add_to_worklist(as->context, wl);
+
+#if defined(ENABLE_TLH) && 0
+ /* XXX hack */
+ method_assumption *as2;
+ as2 = m->assumptions;
+ m->assumptions = NULL;
+ method_break_assumption_monomorphic(as->context, wl);
+ /*
+ assert(m->assumptions == NULL);
+ m->assumptions = as2;*/
+#endif
+
+ }
+}
+
+/* method_printflags ***********************************************************
+
+ Prints the flags of a method to stdout like.
+
+*******************************************************************************/
+
+#if !defined(NDEBUG)
+void method_printflags(methodinfo *m)
+{
+ if (m == NULL) {
+ printf("NULL");
+ return;
+ }
+
+ if (m->flags & ACC_PUBLIC) printf(" PUBLIC");
+ if (m->flags & ACC_PRIVATE) printf(" PRIVATE");
+ if (m->flags & ACC_PROTECTED) printf(" PROTECTED");
+ if (m->flags & ACC_STATIC) printf(" STATIC");
+ if (m->flags & ACC_FINAL) printf(" FINAL");
+ if (m->flags & ACC_SYNCHRONIZED) printf(" SYNCHRONIZED");
+ if (m->flags & ACC_VOLATILE) printf(" VOLATILE");
+ if (m->flags & ACC_TRANSIENT) printf(" TRANSIENT");
+ if (m->flags & ACC_NATIVE) printf(" NATIVE");
+ if (m->flags & ACC_INTERFACE) printf(" INTERFACE");
+ if (m->flags & ACC_ABSTRACT) printf(" ABSTRACT");
+ if (m->flags & ACC_METHOD_BUILTIN) printf(" (builtin)");
+ if (m->flags & ACC_METHOD_MONOMORPHIC) printf(" (mono)");
+ if (m->flags & ACC_METHOD_IMPLEMENTED) printf(" (impl)");
+}
+#endif /* !defined(NDEBUG) */
+
+
+/* method_print ****************************************************************
+
+ Prints a method to stdout like:
+
+ java.lang.Object.<init>()V
+
+*******************************************************************************/
+
+#if !defined(NDEBUG)
+void method_print(methodinfo *m)
+{
+ if (m == NULL) {
+ printf("NULL");
+ return;
+ }
+
+ if (m->clazz != NULL)
+ utf_display_printable_ascii_classname(m->clazz->name);
+ else
+ printf("NULL");
+ printf(".");
+ utf_display_printable_ascii(m->name);
+ utf_display_printable_ascii(m->descriptor);
+
+ method_printflags(m);
+}
+#endif /* !defined(NDEBUG) */
+
+
+/* method_println **************************************************************
+
+ Prints a method plus new line to stdout like:
+
+ java.lang.Object.<init>()V
+
+*******************************************************************************/
+
+#if !defined(NDEBUG)
+void method_println(methodinfo *m)
+{
+ if (opt_debugcolor) printf("\033[31m"); /* red */
+ method_print(m);
+ if (opt_debugcolor) printf("\033[m");
+ printf("\n");
+}
+#endif /* !defined(NDEBUG) */
+
+
+/* method_methodref_print ******************************************************
+
+ Prints a method reference to stdout.
+
+*******************************************************************************/
+
+#if !defined(NDEBUG)
+void method_methodref_print(constant_FMIref *mr)
+{
+ if (!mr) {
+ printf("(constant_FMIref *)NULL");
+ return;
+ }
+
+ if (IS_FMIREF_RESOLVED(mr)) {
+ printf("<method> ");
+ method_print(mr->p.method);
+ }
+ else {
+ printf("<methodref> ");
+ utf_display_printable_ascii_classname(mr->p.classref->name);
+ printf(".");
+ utf_display_printable_ascii(mr->name);
+ utf_display_printable_ascii(mr->descriptor);
+ }
+}
+#endif /* !defined(NDEBUG) */
+
+
+/* method_methodref_println ****************************************************
+
+ Prints a method reference to stdout, followed by a newline.
+
+*******************************************************************************/
+
+#if !defined(NDEBUG)
+void method_methodref_println(constant_FMIref *mr)
+{
+ method_methodref_print(mr);
+ printf("\n");
+}
+#endif /* !defined(NDEBUG) */
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/method.h - method functions header
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _METHOD_H
-#define _METHOD_H
-
-/* forward typedefs ***********************************************************/
-
-typedef struct methodinfo methodinfo;
-typedef struct raw_exception_entry raw_exception_entry;
-typedef struct lineinfo lineinfo;
-typedef struct method_assumption method_assumption;
-typedef struct method_worklist method_worklist;
-typedef struct codeinfo codeinfo;
-
-#include "config.h"
-#include "vm/types.h"
-
-#include "threads/mutex.hpp"
-
-#include "vm/jit/builtin.hpp"
-#include "vm/descriptor.h"
-#include "vm/global.h"
-#include "vm/linker.h"
-#include "vm/loader.hpp"
-#include "vm/references.h"
-
-#if defined(ENABLE_JAVASE)
-# include "vm/stackmap.h"
-#endif
-
-#include "vm/utf8.h"
-
-
-#if defined(ENABLE_REPLACEMENT)
-/* Initial value for the hit countdown field of each method. */
-#define METHOD_INITIAL_HIT_COUNTDOWN 1000
-#endif
-
-
-/* methodinfo *****************************************************************/
-
-struct methodinfo { /* method structure */
- Mutex *mutex; /* we need this in jit's locking */
- s4 flags; /* ACC flags */
- utf *name; /* name of method */
- utf *descriptor; /* JavaVM descriptor string of method */
-#if defined(ENABLE_JAVASE)
- utf *signature; /* Signature attribute */
- stack_map_t *stack_map; /* StackMapTable attribute */
-#endif
-
- methoddesc *parseddesc; /* parsed descriptor */
-
- classinfo *clazz; /* class, the method belongs to */
- s4 vftblindex; /* index of method in virtual function */
- /* table (if it is a virtual method) */
- s4 maxstack; /* maximum stack depth of method */
- s4 maxlocals; /* maximum number of local variables */
- s4 jcodelength; /* length of JavaVM code */
- u1 *jcode; /* pointer to JavaVM code */
-
- s4 rawexceptiontablelength; /* exceptiontable length */
- raw_exception_entry *rawexceptiontable; /* the exceptiontable */
-
- u2 thrownexceptionscount; /* number of exceptions attribute */
- classref_or_classinfo *thrownexceptions; /* except. a method may throw */
-
- u2 linenumbercount; /* number of linenumber attributes */
- lineinfo *linenumbers; /* array of lineinfo items */
-
- u1 *stubroutine; /* stub for compiling or calling natives */
- codeinfo *code; /* current code of this method */
-
-#if defined(ENABLE_LSRA)
- s4 maxlifetimes; /* helper for lsra */
-#endif
-
- methodinfo *overwrites; /* method that is directly overwritten */
- method_assumption *assumptions; /* list of assumptions about this method */
-
-#if defined(ENABLE_REPLACEMENT)
- s4 hitcountdown; /* decreased for each hit */
-#endif
-
-#if defined(ENABLE_DEBUG_FILTER)
- u1 filtermatches; /* flags indicating which filters the method matches */
-#endif
-
-#if defined(ENABLE_ESCAPE)
- u1 *paramescape;
-#endif
-};
-
-/* method_assumption ***********************************************************
-
- This struct is used for registering assumptions about methods.
-
-*******************************************************************************/
-
-struct method_assumption {
- method_assumption *next;
- methodinfo *context;
-};
-
-
-/* method_worklist *************************************************************
-
- List node used for method worklists.
-
-*******************************************************************************/
-
-struct method_worklist {
- method_worklist *next;
- methodinfo *m;
-};
-
-
-/* raw_exception_entry ********************************************************/
-
-/* exception table entry read by the loader */
-
-struct raw_exception_entry { /* exceptiontable entry in a method */
- classref_or_classinfo catchtype; /* catchtype of exc. (0 == catchall) */
- u2 startpc; /* start pc of guarded area (inclusive) */
- u2 endpc; /* end pc of guarded area (exklusive) */
- u2 handlerpc; /* pc of exception handler */
-};
-
-
-/* lineinfo *******************************************************************/
-
-struct lineinfo {
- u2 start_pc;
- u2 line_number;
-};
-
-
-/* global variables ***********************************************************/
-
-extern methodinfo *method_java_lang_reflect_Method_invoke;
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* inline functions ***********************************************************/
-
-inline static bool method_is_builtin(methodinfo* m)
-{
- return m->flags & ACC_METHOD_BUILTIN;
-}
-
-
-/* function prototypes ********************************************************/
-
-void method_init(void);
-
-bool method_load(classbuffer *cb, methodinfo *m, descriptor_pool *descpool);
-void method_free(methodinfo *m);
-bool method_canoverwrite(methodinfo *m, methodinfo *old);
-
-methodinfo *method_new_builtin(builtintable_entry *bte);
-
-methodinfo *method_vftbl_lookup(vftbl_t *vftbl, methodinfo* m);
-
-int32_t method_get_parametercount(methodinfo *m);
-java_handle_objectarray_t *method_get_parametertypearray(methodinfo *m);
-java_handle_objectarray_t *method_get_exceptionarray(methodinfo *m);
-classinfo *method_returntype_get(methodinfo *m);
-
-void method_add_assumption_monomorphic(methodinfo *m, methodinfo *caller);
-void method_break_assumption_monomorphic(methodinfo *m, method_worklist **wl);
-
-s4 method_count_implementations(methodinfo *m, classinfo *c, methodinfo **found);
-
-java_handle_bytearray_t *method_get_annotations(methodinfo *m);
-java_handle_bytearray_t *method_get_parameterannotations(methodinfo *m);
-java_handle_bytearray_t *method_get_annotationdefault(methodinfo *m);
-
-#if !defined(NDEBUG)
-void method_printflags(methodinfo *m);
-void method_print(methodinfo *m);
-void method_println(methodinfo *m);
-void method_methodref_print(constant_FMIref *mr);
-void method_methodref_println(constant_FMIref *mr);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _METHOD_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/method.hpp - method functions header
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _METHOD_H
+#define _METHOD_H
+
+/* forward typedefs ***********************************************************/
+
+typedef struct methodinfo methodinfo;
+typedef struct raw_exception_entry raw_exception_entry;
+typedef struct lineinfo lineinfo;
+typedef struct method_assumption method_assumption;
+typedef struct method_worklist method_worklist;
+typedef struct codeinfo codeinfo;
+
+#include "config.h"
+#include "vm/types.h"
+
+#include "threads/mutex.hpp"
+
+#include "vm/jit/builtin.hpp"
+#include "vm/descriptor.hpp"
+#include "vm/global.h"
+#include "vm/linker.hpp"
+#include "vm/loader.hpp"
+#include "vm/references.h"
+
+#if defined(ENABLE_JAVASE)
+# include "vm/stackmap.h"
+#endif
+
+#include "vm/utf8.h"
+
+
+#if defined(ENABLE_REPLACEMENT)
+/* Initial value for the hit countdown field of each method. */
+#define METHOD_INITIAL_HIT_COUNTDOWN 1000
+#endif
+
+/* methodinfo *****************************************************************/
+
+struct methodinfo { /* method structure */
+ Mutex *mutex; /* we need this in jit's locking */
+ s4 flags; /* ACC flags */
+ utf *name; /* name of method */
+ utf *descriptor; /* JavaVM descriptor string of method */
+#if defined(ENABLE_JAVASE)
+ utf *signature; /* Signature attribute */
+ stack_map_t *stack_map; /* StackMapTable attribute */
+#endif
+
+ methoddesc *parseddesc; /* parsed descriptor */
+
+ classinfo *clazz; /* class, the method belongs to */
+ s4 vftblindex; /* index of method in virtual function */
+ /* table (if it is a virtual method) */
+ s4 maxstack; /* maximum stack depth of method */
+ s4 maxlocals; /* maximum number of local variables */
+ s4 jcodelength; /* length of JavaVM code */
+ u1 *jcode; /* pointer to JavaVM code */
+
+ s4 rawexceptiontablelength; /* exceptiontable length */
+ raw_exception_entry *rawexceptiontable; /* the exceptiontable */
+
+ u2 thrownexceptionscount; /* number of exceptions attribute */
+ classref_or_classinfo *thrownexceptions; /* except. a method may throw */
+
+ u2 linenumbercount; /* number of linenumber attributes */
+ lineinfo *linenumbers; /* array of lineinfo items */
+
+ u1 *stubroutine; /* stub for compiling or calling natives */
+ codeinfo *code; /* current code of this method */
+
+#if defined(ENABLE_LSRA)
+ s4 maxlifetimes; /* helper for lsra */
+#endif
+
+ methodinfo *overwrites; /* method that is directly overwritten */
+ method_assumption *assumptions; /* list of assumptions about this method */
+
+#if defined(ENABLE_REPLACEMENT)
+ s4 hitcountdown; /* decreased for each hit */
+#endif
+
+#if defined(ENABLE_DEBUG_FILTER)
+ u1 filtermatches; /* flags indicating which filters the method matches */
+#endif
+
+#if defined(ENABLE_ESCAPE)
+ u1 *paramescape;
+#endif
+};
+
+/* method_assumption ***********************************************************
+
+ This struct is used for registering assumptions about methods.
+
+*******************************************************************************/
+
+struct method_assumption {
+ method_assumption *next;
+ methodinfo *context;
+};
+
+
+/* method_worklist *************************************************************
+
+ List node used for method worklists.
+
+*******************************************************************************/
+
+struct method_worklist {
+ method_worklist *next;
+ methodinfo *m;
+};
+
+
+/* raw_exception_entry ********************************************************/
+
+/* exception table entry read by the loader */
+
+struct raw_exception_entry { /* exceptiontable entry in a method */
+ classref_or_classinfo catchtype; /* catchtype of exc. (0 == catchall) */
+ u2 startpc; /* start pc of guarded area (inclusive) */
+ u2 endpc; /* end pc of guarded area (exklusive) */
+ u2 handlerpc; /* pc of exception handler */
+};
+
+
+/* lineinfo *******************************************************************/
+
+struct lineinfo {
+ u2 start_pc;
+ u2 line_number;
+};
+
+
+/* global variables ***********************************************************/
+
+extern methodinfo *method_java_lang_reflect_Method_invoke;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* inline functions ***********************************************************/
+
+inline static bool method_is_builtin(methodinfo* m)
+{
+ return m->flags & ACC_METHOD_BUILTIN;
+}
+
+
+/* function prototypes ********************************************************/
+
+void method_init(void);
+
+bool method_load(classbuffer *cb, methodinfo *m, descriptor_pool *descpool);
+void method_free(methodinfo *m);
+bool method_canoverwrite(methodinfo *m, methodinfo *old);
+
+methodinfo *method_new_builtin(builtintable_entry *bte);
+
+methodinfo *method_vftbl_lookup(vftbl_t *vftbl, methodinfo* m);
+
+int32_t method_get_parametercount(methodinfo *m);
+java_handle_objectarray_t *method_get_parametertypearray(methodinfo *m);
+java_handle_objectarray_t *method_get_exceptionarray(methodinfo *m);
+classinfo *method_returntype_get(methodinfo *m);
+
+void method_add_assumption_monomorphic(methodinfo *m, methodinfo *caller);
+void method_break_assumption_monomorphic(methodinfo *m, method_worklist **wl);
+
+s4 method_count_implementations(methodinfo *m, classinfo *c, methodinfo **found);
+
+java_handle_bytearray_t *method_get_annotations(methodinfo *m);
+java_handle_bytearray_t *method_get_parameterannotations(methodinfo *m);
+java_handle_bytearray_t *method_get_annotationdefault(methodinfo *m);
+
+#if !defined(NDEBUG)
+void method_printflags(methodinfo *m);
+void method_print(methodinfo *m);
+void method_println(methodinfo *m);
+void method_methodref_print(constant_FMIref *mr);
+void method_methodref_println(constant_FMIref *mr);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _METHOD_H */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
static inline int mprotect(void* addr, size_t len, int prot);
static inline ssize_t readlink(const char* path, char* buf, size_t bufsiz);
static inline int scandir(const char* dir, struct dirent*** namelist, int(*filter)(const struct dirent*), int(*compar)(const void*, const void*));
+ static inline ssize_t send(int s, const void* buf, size_t len, int flags);
static inline int setsockopt(int s, int level, int optname, const void* optval, socklen_t optlen);
static inline int shutdown(int s, int how);
static inline int socket(int domain, int type, int protocol);
#endif
}
+inline ssize_t os::send(int s, const void* buf, size_t len, int flags)
+{
+ // TODO Should be restartable on Linux and interruptible on Solaris.
+#if defined(HAVE_SEND)
+ return ::send(s, buf, len, flags);
+#else
+# error send not available
+#endif
+}
+
inline int os::setsockopt(int s, int level, int optname, const void* optval, socklen_t optlen)
{
#if defined(HAVE_SETSOCKOPT)
#include "vm/class.hpp"
#include "vm/global.h"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/utf8.h"
#include "vm/class.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/os.hpp"
#include "vm/properties.hpp"
private:
std::map<const char*, const char*, ltstr> _properties;
+private:
+ // Don't allow to copy the properties.
+ Properties(const Properties&);
+
public:
Properties();
#include "vm/types.h"
#include "vm/class.hpp"
-#include "vm/descriptor.h"
+#include "vm/descriptor.hpp"
#include "vm/field.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/utf8.h"
#include "mm/memory.h"
-#include "vm/access.h"
-#include "vm/classcache.h"
-#include "vm/descriptor.h"
+#include "vm/access.hpp"
+#include "vm/classcache.hpp"
+#include "vm/descriptor.hpp"
#include "vm/exceptions.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
-#include "vm/linker.h"
+#include "vm/linker.hpp"
#include "vm/loader.hpp"
#include "vm/options.h"
#include "vm/primitive.hpp"
#include "vm/resolve.hpp"
#include "vm/jit/jit.hpp"
-#include "vm/jit/verify/typeinfo.h"
+#include "vm/jit/verify/typeinfo.hpp"
/******************************************************************************/
#include "vm/class.hpp"
#include "vm/field.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/references.h"
#include "vm/jit/jit.hpp"
#include "vm/jit/reg.h"
#include "vm/jit/ir/instruction.hpp"
-#include "vm/jit/verify/typeinfo.h"
+#include "vm/jit/verify/typeinfo.hpp"
/* constants ******************************************************************/
#include "vm/exceptions.hpp"
#include "vm/globals.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/signallocal.h"
#include "vm/vm.hpp"
#include "vm/class.hpp"
#include "vm/exceptions.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/stackmap.h"
#include "vm/statistics.h"
#include "vm/global.h"
#include "vm/loader.hpp"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* verification_type_info *****************************************************/
full_frame_t full_frame;
};
+#if defined(__cplusplus)
+extern "C" {
+#endif
/* function prototypes ********************************************************/
bool stackmap_load_attribute_stackmaptable(classbuffer *cb, methodinfo *m);
+#if defined(__cplusplus)
+}
+#endif
+
#endif /* _STACKMAP_H */
#include "vm/class.hpp"
#include "vm/field.hpp"
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
#include "vm/options.h"
#include "vm/statistics.h"
#include "vm/properties.hpp"
#include "vm/suck.hpp"
#include "vm/vm.hpp"
-#include "vm/zip.h"
+#include "vm/zip.hpp"
/* global variables ***********************************************************/
s4 namlen;
char *p;
- /* get the property value */
- Properties properties = VM::get_current()->get_properties();
+ // Get the property value.
+ Properties& properties = VM::get_current()->get_properties();
value = properties.get(key);
if (value == NULL)
#endif
#include "vm/jit/builtin.hpp"
-#include "vm/classcache.h"
+#include "vm/classcache.hpp"
#include "vm/exceptions.hpp"
-#include "vm/finalizer.h"
+#include "vm/finalizer.hpp"
#include "vm/global.h"
#include "vm/globals.hpp"
#include "vm/initialize.hpp"
OPT_MS,
OPT_MX,
+ OPT_XCHECK_JNI,
+
/* CACAO options */
OPT_VERBOSE1,
#if defined(ENABLE_VERIFIER)
OPT_NOVERIFY,
+ OPT_XVERIFY_ALL,
+ OPT_XVERIFY_NONE,
#if defined(TYPECHECK_VERBOSE)
OPT_VERBOSETC,
#endif
{ "noasyncgc", false, OPT_IGNORE },
#if defined(ENABLE_VERIFIER)
{ "noverify", false, OPT_NOVERIFY },
- { "Xverify:none", false, OPT_NOVERIFY },
+ { "Xverify:all", false, OPT_XVERIFY_ALL },
+ { "Xverify:none", false, OPT_XVERIFY_NONE },
#endif
{ "v", false, OPT_VERBOSE1 },
{ "verbose:", true, OPT_VERBOSE },
{ "Xss", true, OPT_SS },
{ "ss", true, OPT_SS },
+ { "Xcheck:jni", false, OPT_XCHECK_JNI },
+
#if defined(ENABLE_PROFILING)
{ "Xprof:", true, OPT_PROF_OPTION },
{ "Xprof", false, OPT_PROF },
}
break;
+ case OPT_XCHECK_JNI:
+ // HotSpot compatibility option.
+ break;
+
case OPT_VERBOSE1:
opt_verbose = true;
break;
break;
#if defined(ENABLE_VERIFIER)
+ case OPT_XVERIFY_ALL:
+ opt_verify = true;
+ break;
+
case OPT_NOVERIFY:
+ case OPT_XVERIFY_NONE:
opt_verify = false;
break;
#endif
// Includes.
#include "vm/global.h"
-#include "vm/method.h"
+#include "vm/method.hpp"
/* These C methods are the exported interface. ********************************/
+++ /dev/null
-/* src/vm/zip.c - ZIP file handling for bootstrap classloader
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#include "config.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <zlib.h>
-#include <sys/mman.h>
-
-#include "vm/types.h"
-
-#include "vm/descriptor.h" /* needed to prevent circular dependency */
-#include "toolbox/hashtable.h"
-
-#include "mm/memory.h"
-
-#include "vm/global.h"
-#include "vm/suck.hpp"
-#include "vm/utf8.h"
-#include "vm/vm.hpp"
-#include "vm/zip.h"
-
-
-/* start size for classes hashtable *******************************************/
-
-#define HASHTABLE_CLASSES_SIZE (1 << 10)
-
-
-/* info taken from:
- http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
-*/
-
-/* all signatures in the ZIP file have a length of 4 bytes ********************/
-
-#define SIGNATURE_LENGTH 4
-
-/* Central directory structure *************************************************
-
- [file header 1]
- .
- .
- .
- [file header n]
- [digital signature]
-
- File header:
-
- central file header signature 4 bytes (0x02014b50)
- version made by 2 bytes
- version needed to extract 2 bytes
- general purpose bit flag 2 bytes
- compression method 2 bytes
- last mod file time 2 bytes
- last mod file date 2 bytes
- crc-32 4 bytes
- compressed size 4 bytes
- uncompressed size 4 bytes
- file name length 2 bytes
- extra field length 2 bytes
- file comment length 2 bytes
- disk number start 2 bytes
- internal file attributes 2 bytes
- external file attributes 4 bytes
- relative offset of local header 4 bytes
-
- file name (variable size)
- extra field (variable size)
- file comment (variable size)
-
- Digital signature:
-
- header signature 4 bytes (0x05054b50)
- size of data 2 bytes
- signature data (variable size)
-
-*******************************************************************************/
-
-#define CDSFH_HEADER_SIZE 46
-
-#define CDSFH_SIGNATURE 0x02014b50
-#define CDSFH_COMPRESSION_METHOD 10
-#define CDSFH_COMPRESSED_SIZE 20
-#define CDSFH_UNCOMPRESSED_SIZE 24
-#define CDSFH_FILE_NAME_LENGTH 28
-#define CDSFH_EXTRA_FIELD_LENGTH 30
-#define CDSFH_FILE_COMMENT_LENGTH 32
-#define CDSFH_RELATIVE_OFFSET 42
-#define CDSFH_FILENAME 46
-
-typedef struct cdsfh cdsfh;
-
-struct cdsfh {
- u2 compressionmethod;
- u4 compressedsize;
- u4 uncompressedsize;
- u2 filenamelength;
- u2 extrafieldlength;
- u2 filecommentlength;
- u4 relativeoffset;
-};
-
-
-/* End of central directory record *********************************************
-
- end of central dir signature 4 bytes (0x06054b50)
- number of this disk 2 bytes
- number of the disk with the
- start of the central directory 2 bytes
- total number of entries in the
- central directory on this disk 2 bytes
- total number of entries in
- the central directory 2 bytes
- size of the central directory 4 bytes
- offset of start of central
- directory with respect to
- the starting disk number 4 bytes
- .ZIP file comment length 2 bytes
- .ZIP file comment (variable size)
-
-*******************************************************************************/
-
-#define EOCDR_SIGNATURE 0x06054b50
-#define EOCDR_ENTRIES 10
-#define EOCDR_OFFSET 16
-
-typedef struct eocdr eocdr;
-
-struct eocdr {
- u2 entries;
- u4 offset;
-};
-
-
-/* zip_open ********************************************************************
-
- XXX
-
-*******************************************************************************/
-
-hashtable *zip_open(char *path)
-{
- hashtable *ht;
- hashtable_zipfile_entry *htzfe;
- int fd;
- u1 lfh_signature[SIGNATURE_LENGTH];
- off_t len;
- u1 *filep;
- s4 i;
- u1 *p;
- eocdr eocdr;
- cdsfh cdsfh;
- const char *filename;
- const char *classext;
- utf *u;
- u4 key; /* hashkey computed from utf-text */
- u4 slot; /* slot in hashtable */
-
- /* first of all, open the file */
-
- if ((fd = open(path, O_RDONLY)) == -1)
- return NULL;
-
- /* check for signature in first local file header */
-
- if (read(fd, lfh_signature, SIGNATURE_LENGTH) != SIGNATURE_LENGTH)
- return NULL;
-
- if (SUCK_LE_U4(lfh_signature) != LFH_SIGNATURE)
- return NULL;
-
- /* get the file length */
-
- if ((len = lseek(fd, 0, SEEK_END)) == -1)
- return NULL;
-
- /* we better mmap the file */
-
- filep = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
-
- /* some older compilers, like DEC OSF cc, don't like comparisons
- on void* types */
-
- if ((ptrint) filep == (ptrint) MAP_FAILED)
- return NULL;
-
- /* find end of central directory record */
-
- for (p = filep + len; p >= filep; p--)
- if (SUCK_LE_U4(p) == EOCDR_SIGNATURE)
- break;
-
- /* get number of entries in central directory */
-
- eocdr.entries = SUCK_LE_U2(p + EOCDR_ENTRIES);
- eocdr.offset = SUCK_LE_U4(p + EOCDR_OFFSET);
-
- /* create hashtable for filenames */
-
- ht = NEW(hashtable);
-
- hashtable_create(ht, HASHTABLE_CLASSES_SIZE);
-
- /* add all file entries into the hashtable */
-
- for (i = 0, p = filep + eocdr.offset; i < eocdr.entries; i++) {
- /* check file header signature */
-
- if (SUCK_LE_U4(p) != CDSFH_SIGNATURE)
- return NULL;
-
- /* we found an entry */
-
- cdsfh.compressionmethod = SUCK_LE_U2(p + CDSFH_COMPRESSION_METHOD);
- cdsfh.compressedsize = SUCK_LE_U4(p + CDSFH_COMPRESSED_SIZE);
- cdsfh.uncompressedsize = SUCK_LE_U4(p + CDSFH_UNCOMPRESSED_SIZE);
- cdsfh.filenamelength = SUCK_LE_U2(p + CDSFH_FILE_NAME_LENGTH);
- cdsfh.extrafieldlength = SUCK_LE_U2(p + CDSFH_EXTRA_FIELD_LENGTH);
- cdsfh.filecommentlength = SUCK_LE_U2(p + CDSFH_FILE_COMMENT_LENGTH);
- cdsfh.relativeoffset = SUCK_LE_U4(p + CDSFH_RELATIVE_OFFSET);
-
- /* create utf8 string of filename, strip .class from classes */
-
- filename = (const char *) (p + CDSFH_FILENAME);
- classext = filename + cdsfh.filenamelength - strlen(".class");
-
- /* skip directory entries */
-
- if (filename[cdsfh.filenamelength - 1] != '/') {
- if (strncmp(classext, ".class", strlen(".class")) == 0)
- u = utf_new(filename, cdsfh.filenamelength - strlen(".class"));
- else
- u = utf_new(filename, cdsfh.filenamelength);
-
- /* insert class into hashtable */
-
- htzfe = NEW(hashtable_zipfile_entry);
-
- htzfe->filename = u;
- htzfe->compressionmethod = cdsfh.compressionmethod;
- htzfe->compressedsize = cdsfh.compressedsize;
- htzfe->uncompressedsize = cdsfh.uncompressedsize;
- htzfe->data = filep + cdsfh.relativeoffset;
-
- /* get hashtable slot */
-
- key = utf_hashkey(u->text, u->blength);
- slot = key & (ht->size - 1);
-
- /* insert into external chain */
-
- htzfe->hashlink = ht->ptr[slot];
-
- /* insert hashtable zipfile entry */
-
- ht->ptr[slot] = htzfe;
- ht->entries++;
- }
-
- /* move to next central directory structure file header */
-
- p = p +
- CDSFH_HEADER_SIZE +
- cdsfh.filenamelength +
- cdsfh.extrafieldlength +
- cdsfh.filecommentlength;
- }
-
- /* return pointer to hashtable */
-
- return ht;
-}
-
-
-/* zip_find ********************************************************************
-
- Search for the given filename in the classpath entries of a zip file.
-
- NOTE: The '.class' extension is stripped when reading a zip file, so if
- you want to find a .class file, you must search for its name _without_
- the '.class' extension.
- XXX I dont like that, it makes foo and foo.class ambiguous. -Edwin
-
- IN:
- lce..........the classpath entries for the zip file
- u............the filename to look for
-
- RETURN VALUE:
- hashtable_zipfile_entry * of the entry if found, or
- NULL if not found
-
-*******************************************************************************/
-
-hashtable_zipfile_entry *zip_find(list_classpath_entry *lce, utf *u)
-{
- hashtable *ht;
- u4 key; /* hashkey computed from utf-text */
- u4 slot; /* slot in hashtable */
- hashtable_zipfile_entry *htzfe; /* hashtable element */
-
- /* get classes hashtable from the classpath entry */
-
- ht = lce->htclasses;
-
- /* get the hashtable slot of the name searched */
-
- key = utf_hashkey(u->text, u->blength);
- slot = key & (ht->size - 1);
- htzfe = ht->ptr[slot];
-
- /* search external hash chain for utf-symbol */
-
- while (htzfe) {
- if (htzfe->filename == u)
- return htzfe;
-
- /* next element in external chain */
-
- htzfe = htzfe->hashlink;
- }
-
- /* file not found in this archive */
-
- return NULL;
-}
-
-
-/* zip_get ********************************************************************
-
- XXX
-
-*******************************************************************************/
-
-classbuffer *zip_get(list_classpath_entry *lce, classinfo *c)
-{
- hashtable_zipfile_entry *htzfe;
- lfh lfh;
- u1 *indata;
- u1 *outdata;
- z_stream zs;
- int err;
- classbuffer *cb;
-
- /* try to find the class in the current archive */
-
- htzfe = zip_find(lce, c->name);
-
- if (htzfe == NULL)
- return NULL;
-
- /* read stuff from local file header */
-
- lfh.filenamelength = SUCK_LE_U2(htzfe->data + LFH_FILE_NAME_LENGTH);
- lfh.extrafieldlength = SUCK_LE_U2(htzfe->data + LFH_EXTRA_FIELD_LENGTH);
-
- indata = htzfe->data +
- LFH_HEADER_SIZE +
- lfh.filenamelength +
- lfh.extrafieldlength;
-
- /* allocate buffer for uncompressed data */
-
- outdata = MNEW(u1, htzfe->uncompressedsize);
-
- /* how is the file stored? */
-
- switch (htzfe->compressionmethod) {
- case Z_DEFLATED:
- /* fill z_stream structure */
-
- zs.next_in = indata;
- zs.avail_in = htzfe->compressedsize;
- zs.next_out = outdata;
- zs.avail_out = htzfe->uncompressedsize;
-
- zs.zalloc = Z_NULL;
- zs.zfree = Z_NULL;
- zs.opaque = Z_NULL;
-
- /* initialize this inflate run */
-
- if (inflateInit2(&zs, -MAX_WBITS) != Z_OK)
- vm_abort("zip_get: inflateInit2 failed: %s", strerror(errno));
-
- /* decompress the file into buffer */
-
- err = inflate(&zs, Z_SYNC_FLUSH);
-
- if ((err != Z_STREAM_END) && (err != Z_OK))
- vm_abort("zip_get: inflate failed: %s", strerror(errno));
-
- /* finish this inflate run */
-
- if (inflateEnd(&zs) != Z_OK)
- vm_abort("zip_get: inflateEnd failed: %s", strerror(errno));
- break;
-
- case 0:
- /* uncompressed file, just copy the data */
- MCOPY(outdata, indata, u1, htzfe->compressedsize);
- break;
-
- default:
- vm_abort("zip_get: unknown compression method %d",
- htzfe->compressionmethod);
- }
-
- /* allocate classbuffer */
-
- cb = NEW(classbuffer);
-
- cb->clazz = c;
- cb->size = htzfe->uncompressedsize;
- cb->data = outdata;
- cb->pos = outdata;
- cb->path = lce->path;
-
- /* return the filled classbuffer structure */
-
- return cb;
-}
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
--- /dev/null
+/* src/vm/zip.cpp - ZIP file handling for bootstrap classloader
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <zlib.h>
+#include <sys/mman.h>
+
+#include "vm/types.h"
+
+#include "vm/descriptor.hpp" /* needed to prevent circular dependency */
+#include "toolbox/hashtable.h"
+
+#include "mm/memory.h"
+
+#include "vm/global.h"
+#include "vm/suck.hpp"
+#include "vm/utf8.h"
+#include "vm/vm.hpp"
+#include "vm/zip.hpp"
+
+
+/* start size for classes hashtable *******************************************/
+
+#define HASHTABLE_CLASSES_SIZE (1 << 10)
+
+
+/* info taken from:
+ http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
+*/
+
+/* all signatures in the ZIP file have a length of 4 bytes ********************/
+
+#define SIGNATURE_LENGTH 4
+
+/* Central directory structure *************************************************
+
+ [file header 1]
+ .
+ .
+ .
+ [file header n]
+ [digital signature]
+
+ File header:
+
+ central file header signature 4 bytes (0x02014b50)
+ version made by 2 bytes
+ version needed to extract 2 bytes
+ general purpose bit flag 2 bytes
+ compression method 2 bytes
+ last mod file time 2 bytes
+ last mod file date 2 bytes
+ crc-32 4 bytes
+ compressed size 4 bytes
+ uncompressed size 4 bytes
+ file name length 2 bytes
+ extra field length 2 bytes
+ file comment length 2 bytes
+ disk number start 2 bytes
+ internal file attributes 2 bytes
+ external file attributes 4 bytes
+ relative offset of local header 4 bytes
+
+ file name (variable size)
+ extra field (variable size)
+ file comment (variable size)
+
+ Digital signature:
+
+ header signature 4 bytes (0x05054b50)
+ size of data 2 bytes
+ signature data (variable size)
+
+*******************************************************************************/
+
+#define CDSFH_HEADER_SIZE 46
+
+#define CDSFH_SIGNATURE 0x02014b50
+#define CDSFH_COMPRESSION_METHOD 10
+#define CDSFH_COMPRESSED_SIZE 20
+#define CDSFH_UNCOMPRESSED_SIZE 24
+#define CDSFH_FILE_NAME_LENGTH 28
+#define CDSFH_EXTRA_FIELD_LENGTH 30
+#define CDSFH_FILE_COMMENT_LENGTH 32
+#define CDSFH_RELATIVE_OFFSET 42
+#define CDSFH_FILENAME 46
+
+typedef struct cdsfh cdsfh;
+
+struct cdsfh {
+ u2 compressionmethod;
+ u4 compressedsize;
+ u4 uncompressedsize;
+ u2 filenamelength;
+ u2 extrafieldlength;
+ u2 filecommentlength;
+ u4 relativeoffset;
+};
+
+
+/* End of central directory record *********************************************
+
+ end of central dir signature 4 bytes (0x06054b50)
+ number of this disk 2 bytes
+ number of the disk with the
+ start of the central directory 2 bytes
+ total number of entries in the
+ central directory on this disk 2 bytes
+ total number of entries in
+ the central directory 2 bytes
+ size of the central directory 4 bytes
+ offset of start of central
+ directory with respect to
+ the starting disk number 4 bytes
+ .ZIP file comment length 2 bytes
+ .ZIP file comment (variable size)
+
+*******************************************************************************/
+
+#define EOCDR_SIGNATURE 0x06054b50
+#define EOCDR_ENTRIES 10
+#define EOCDR_OFFSET 16
+
+typedef struct eocdr eocdr;
+
+struct eocdr {
+ u2 entries;
+ u4 offset;
+};
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+/* zip_open ********************************************************************
+
+ XXX
+
+*******************************************************************************/
+
+hashtable *zip_open(char *path)
+{
+ hashtable *ht;
+ hashtable_zipfile_entry *htzfe;
+ int fd;
+ u1 lfh_signature[SIGNATURE_LENGTH];
+ off_t len;
+ u1 *filep;
+ s4 i;
+ u1 *p;
+ eocdr eocdr;
+ cdsfh cdsfh;
+ const char *filename;
+ const char *classext;
+ utf *u;
+ u4 key; /* hashkey computed from utf-text */
+ u4 slot; /* slot in hashtable */
+
+ /* first of all, open the file */
+
+ if ((fd = open(path, O_RDONLY)) == -1)
+ return NULL;
+
+ /* check for signature in first local file header */
+
+ if (read(fd, lfh_signature, SIGNATURE_LENGTH) != SIGNATURE_LENGTH)
+ return NULL;
+
+ if (SUCK_LE_U4(lfh_signature) != LFH_SIGNATURE)
+ return NULL;
+
+ /* get the file length */
+
+ if ((len = lseek(fd, 0, SEEK_END)) == -1)
+ return NULL;
+
+ /* we better mmap the file */
+
+ filep = (u1*) mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ /* some older compilers, like DEC OSF cc, don't like comparisons
+ on void* types */
+
+ if ((ptrint) filep == (ptrint) MAP_FAILED)
+ return NULL;
+
+ /* find end of central directory record */
+
+ for (p = filep + len; p >= filep; p--)
+ if (SUCK_LE_U4(p) == EOCDR_SIGNATURE)
+ break;
+
+ /* get number of entries in central directory */
+
+ eocdr.entries = SUCK_LE_U2(p + EOCDR_ENTRIES);
+ eocdr.offset = SUCK_LE_U4(p + EOCDR_OFFSET);
+
+ /* create hashtable for filenames */
+
+ ht = NEW(hashtable);
+
+ hashtable_create(ht, HASHTABLE_CLASSES_SIZE);
+
+ /* add all file entries into the hashtable */
+
+ for (i = 0, p = filep + eocdr.offset; i < eocdr.entries; i++) {
+ /* check file header signature */
+
+ if (SUCK_LE_U4(p) != CDSFH_SIGNATURE)
+ return NULL;
+
+ /* we found an entry */
+
+ cdsfh.compressionmethod = SUCK_LE_U2(p + CDSFH_COMPRESSION_METHOD);
+ cdsfh.compressedsize = SUCK_LE_U4(p + CDSFH_COMPRESSED_SIZE);
+ cdsfh.uncompressedsize = SUCK_LE_U4(p + CDSFH_UNCOMPRESSED_SIZE);
+ cdsfh.filenamelength = SUCK_LE_U2(p + CDSFH_FILE_NAME_LENGTH);
+ cdsfh.extrafieldlength = SUCK_LE_U2(p + CDSFH_EXTRA_FIELD_LENGTH);
+ cdsfh.filecommentlength = SUCK_LE_U2(p + CDSFH_FILE_COMMENT_LENGTH);
+ cdsfh.relativeoffset = SUCK_LE_U4(p + CDSFH_RELATIVE_OFFSET);
+
+ /* create utf8 string of filename, strip .class from classes */
+
+ filename = (const char *) (p + CDSFH_FILENAME);
+ classext = filename + cdsfh.filenamelength - strlen(".class");
+
+ /* skip directory entries */
+
+ if (filename[cdsfh.filenamelength - 1] != '/') {
+ if (strncmp(classext, ".class", strlen(".class")) == 0)
+ u = utf_new(filename, cdsfh.filenamelength - strlen(".class"));
+ else
+ u = utf_new(filename, cdsfh.filenamelength);
+
+ /* insert class into hashtable */
+
+ htzfe = NEW(hashtable_zipfile_entry);
+
+ htzfe->filename = u;
+ htzfe->compressionmethod = cdsfh.compressionmethod;
+ htzfe->compressedsize = cdsfh.compressedsize;
+ htzfe->uncompressedsize = cdsfh.uncompressedsize;
+ htzfe->data = filep + cdsfh.relativeoffset;
+
+ /* get hashtable slot */
+
+ key = utf_hashkey(u->text, u->blength);
+ slot = key & (ht->size - 1);
+
+ /* insert into external chain */
+
+ htzfe->hashlink = (hashtable_zipfile_entry*) ht->ptr[slot];
+
+ /* insert hashtable zipfile entry */
+
+ ht->ptr[slot] = htzfe;
+ ht->entries++;
+ }
+
+ /* move to next central directory structure file header */
+
+ p = p +
+ CDSFH_HEADER_SIZE +
+ cdsfh.filenamelength +
+ cdsfh.extrafieldlength +
+ cdsfh.filecommentlength;
+ }
+
+ /* return pointer to hashtable */
+
+ return ht;
+}
+
+
+/* zip_find ********************************************************************
+
+ Search for the given filename in the classpath entries of a zip file.
+
+ NOTE: The '.class' extension is stripped when reading a zip file, so if
+ you want to find a .class file, you must search for its name _without_
+ the '.class' extension.
+ XXX I dont like that, it makes foo and foo.class ambiguous. -Edwin
+
+ IN:
+ lce..........the classpath entries for the zip file
+ u............the filename to look for
+
+ RETURN VALUE:
+ hashtable_zipfile_entry * of the entry if found, or
+ NULL if not found
+
+*******************************************************************************/
+
+hashtable_zipfile_entry *zip_find(list_classpath_entry *lce, utf *u)
+{
+ hashtable *ht;
+ u4 key; /* hashkey computed from utf-text */
+ u4 slot; /* slot in hashtable */
+ hashtable_zipfile_entry *htzfe; /* hashtable element */
+
+ /* get classes hashtable from the classpath entry */
+
+ ht = lce->htclasses;
+
+ /* get the hashtable slot of the name searched */
+
+ key = utf_hashkey(u->text, u->blength);
+ slot = key & (ht->size - 1);
+ htzfe = (hashtable_zipfile_entry*) ht->ptr[slot];
+
+ /* search external hash chain for utf-symbol */
+
+ while (htzfe) {
+ if (htzfe->filename == u)
+ return htzfe;
+
+ /* next element in external chain */
+
+ htzfe = htzfe->hashlink;
+ }
+
+ /* file not found in this archive */
+
+ return NULL;
+}
+
+
+/* zip_get ********************************************************************
+
+ XXX
+
+*******************************************************************************/
+
+classbuffer *zip_get(list_classpath_entry *lce, classinfo *c)
+{
+ hashtable_zipfile_entry *htzfe;
+ lfh lfh;
+ u1 *indata;
+ u1 *outdata;
+ z_stream zs;
+ int err;
+ classbuffer *cb;
+
+ /* try to find the class in the current archive */
+
+ htzfe = zip_find(lce, c->name);
+
+ if (htzfe == NULL)
+ return NULL;
+
+ /* read stuff from local file header */
+
+ lfh.filenamelength = SUCK_LE_U2(htzfe->data + LFH_FILE_NAME_LENGTH);
+ lfh.extrafieldlength = SUCK_LE_U2(htzfe->data + LFH_EXTRA_FIELD_LENGTH);
+
+ indata = htzfe->data +
+ LFH_HEADER_SIZE +
+ lfh.filenamelength +
+ lfh.extrafieldlength;
+
+ /* allocate buffer for uncompressed data */
+
+ outdata = MNEW(u1, htzfe->uncompressedsize);
+
+ /* how is the file stored? */
+
+ switch (htzfe->compressionmethod) {
+ case Z_DEFLATED:
+ /* fill z_stream structure */
+
+ zs.next_in = indata;
+ zs.avail_in = htzfe->compressedsize;
+ zs.next_out = outdata;
+ zs.avail_out = htzfe->uncompressedsize;
+
+ zs.zalloc = Z_NULL;
+ zs.zfree = Z_NULL;
+ zs.opaque = Z_NULL;
+
+ /* initialize this inflate run */
+
+ if (inflateInit2(&zs, -MAX_WBITS) != Z_OK)
+ vm_abort("zip_get: inflateInit2 failed: %s", strerror(errno));
+
+ /* decompress the file into buffer */
+
+ err = inflate(&zs, Z_SYNC_FLUSH);
+
+ if ((err != Z_STREAM_END) && (err != Z_OK))
+ vm_abort("zip_get: inflate failed: %s", strerror(errno));
+
+ /* finish this inflate run */
+
+ if (inflateEnd(&zs) != Z_OK)
+ vm_abort("zip_get: inflateEnd failed: %s", strerror(errno));
+ break;
+
+ case 0:
+ /* uncompressed file, just copy the data */
+ MCOPY(outdata, indata, u1, htzfe->compressedsize);
+ break;
+
+ default:
+ vm_abort("zip_get: unknown compression method %d",
+ htzfe->compressionmethod);
+ }
+
+ /* allocate classbuffer */
+
+ cb = NEW(classbuffer);
+
+ cb->clazz = c;
+ cb->size = htzfe->uncompressedsize;
+ cb->data = outdata;
+ cb->pos = outdata;
+ cb->path = lce->path;
+
+ /* return the filled classbuffer structure */
+
+ return cb;
+}
+
+#if defined(__cplusplus)
+}
+#endif
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
+++ /dev/null
-/* src/vm/zip.c - ZIP file handling for bootstrap classloader
-
- Copyright (C) 1996-2005, 2006, 2007, 2008
- CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
-
- This file is part of CACAO.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301, USA.
-
-*/
-
-
-#ifndef _ZIP_H
-#define _ZIP_H
-
-#include "config.h"
-#include "vm/types.h"
-
-#include "toolbox/hashtable.h"
-
-#include "vm/class.hpp"
-#include "vm/global.h"
-#include "vm/loader.hpp"
-#include "vm/suck.hpp"
-#include "vm/utf8.h"
-
-
-/* Local file header ***********************************************************
-
- local file header signature 4 bytes (0x04034b50)
- version needed to extract 2 bytes
- general purpose bit flag 2 bytes
- compression method 2 bytes
- last mod file time 2 bytes
- last mod file date 2 bytes
- crc-32 4 bytes
- compressed size 4 bytes
- uncompressed size 4 bytes
- file name length 2 bytes
- extra field length 2 bytes
-
- file name (variable size)
- extra field (variable size)
-
-*******************************************************************************/
-
-#define LFH_HEADER_SIZE 30
-
-#define LFH_SIGNATURE 0x04034b50
-#define LFH_FILE_NAME_LENGTH 26
-#define LFH_EXTRA_FIELD_LENGTH 28
-
-typedef struct lfh lfh;
-
-struct lfh {
- u2 compressionmethod;
- u4 compressedsize;
- u4 uncompressedsize;
- u2 filenamelength;
- u2 extrafieldlength;
-};
-
-/* hashtable_zipfile_entry ****************************************************/
-
-typedef struct hashtable_zipfile_entry hashtable_zipfile_entry;
-
-struct hashtable_zipfile_entry {
- utf *filename;
- u2 compressionmethod;
- u4 compressedsize;
- u4 uncompressedsize;
- u1 *data;
- hashtable_zipfile_entry *hashlink;
-};
-
-
-/* function prototypes ********************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-hashtable *zip_open(char *path);
-hashtable_zipfile_entry *zip_find(list_classpath_entry *lce, utf *u);
-classbuffer *zip_get(list_classpath_entry *lce, classinfo *c);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZIP_H */
-
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: c
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- */
--- /dev/null
+/* src/vm/zip.cpp - ZIP file handling for bootstrap classloader
+
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#ifndef _ZIP_HPP
+#define _ZIP_HPP
+
+#include "config.h"
+#include "vm/types.h"
+
+#include "toolbox/hashtable.h"
+
+#include "vm/class.hpp"
+#include "vm/global.h"
+#include "vm/loader.hpp"
+#include "vm/suck.hpp"
+#include "vm/utf8.h"
+
+
+/* Local file header ***********************************************************
+
+ local file header signature 4 bytes (0x04034b50)
+ version needed to extract 2 bytes
+ general purpose bit flag 2 bytes
+ compression method 2 bytes
+ last mod file time 2 bytes
+ last mod file date 2 bytes
+ crc-32 4 bytes
+ compressed size 4 bytes
+ uncompressed size 4 bytes
+ file name length 2 bytes
+ extra field length 2 bytes
+
+ file name (variable size)
+ extra field (variable size)
+
+*******************************************************************************/
+
+#define LFH_HEADER_SIZE 30
+
+#define LFH_SIGNATURE 0x04034b50
+#define LFH_FILE_NAME_LENGTH 26
+#define LFH_EXTRA_FIELD_LENGTH 28
+
+typedef struct lfh lfh;
+
+struct lfh {
+ u2 compressionmethod;
+ u4 compressedsize;
+ u4 uncompressedsize;
+ u2 filenamelength;
+ u2 extrafieldlength;
+};
+
+/* hashtable_zipfile_entry ****************************************************/
+
+typedef struct hashtable_zipfile_entry hashtable_zipfile_entry;
+
+struct hashtable_zipfile_entry {
+ utf *filename;
+ u2 compressionmethod;
+ u4 compressedsize;
+ u4 uncompressedsize;
+ u1 *data;
+ hashtable_zipfile_entry *hashlink;
+};
+
+
+/* function prototypes ********************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+hashtable *zip_open(char *path);
+hashtable_zipfile_entry *zip_find(list_classpath_entry *lce, utf *u);
+classbuffer *zip_get(list_classpath_entry *lce, classinfo *c);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZIP_HPP */
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c++
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
PR58.class,
PR65.class,
PR80.class,
-PR89.class
+PR89.class,
+PR112.class
})
public class All {
--- /dev/null
+/* tests/regression/bugzilla/PR112.java
+
+ Copyright (C) 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.lang.reflect.*;
+
+public class PR112 {
+ @Test ( expected = IllegalArgumentException.class )
+ public void test() throws IllegalArgumentException {
+ // Derived from OpenJDK's jdk jtreg test
+ // java/lang/reflect/Array/ExceedMaxDim.java
+ Object o = Array.newInstance(Integer.TYPE, 0);
+
+ for (int i = 1; i <= 254; i++) {
+ o = Array.newInstance(o.getClass(), 1);
+ }
+
+ o = Array.newInstance(o.getClass(), 1);
+ }
+}