carriage returns removed from sources

This commit is contained in:
Andy Clement
2015-03-15 21:37:23 -07:00
parent a36a14527c
commit 22bc5e3c02
38 changed files with 11065 additions and 11065 deletions

View File

@@ -1,90 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1,10 +1,10 @@
Manifest-Version: 1.0
Specification-Title: SpringLoaded Agent
Specification-Version: 1.0.0
Specification-Vendor: SpringSource
Implementation-Title: org.springsource.loaded
Implementation-Version: 1.0.0
Implementation-Vendor: SpringSource
Premain-Class: org.springsource.loaded.agent.SpringLoadedAgent
Agent-Class: org.springsource.loaded.agent.SpringLoadedAgent
Can-Redefine-Classes: true
Manifest-Version: 1.0
Specification-Title: SpringLoaded Agent
Specification-Version: 1.0.0
Specification-Vendor: SpringSource
Implementation-Title: org.springsource.loaded
Implementation-Version: 1.0.0
Implementation-Vendor: SpringSource
Premain-Class: org.springsource.loaded.agent.SpringLoadedAgent
Agent-Class: org.springsource.loaded.agent.SpringLoadedAgent
Can-Redefine-Classes: true

View File

@@ -1,38 +1,38 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Represents '*' type pattern.
*
* @author Andy Clement
* @since 0.5.0
*/
public class AnyTypePattern extends TypePattern {
public AnyTypePattern() {
}
protected boolean internalMatches(String input) {
return true;
}
public String toString() {
return "text:*";
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Represents '*' type pattern.
*
* @author Andy Clement
* @since 0.5.0
*/
public class AnyTypePattern extends TypePattern {
public AnyTypePattern() {
}
protected boolean internalMatches(String input) {
return true;
}
public String toString() {
return "text:*";
}
}

View File

@@ -1,324 +1,324 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* Modify a class by changing it from one name to another. References to other types can also be changed. Basically used
* in the test suite.
*
* @author Andy Clement
* @since 0.5.0
*/
public class ClassRenamer {
/**
* Rename a type - changing it to specified new name (which should be the dotted form of the name). Retargets are an
* optional sequence of retargets to also perform during the rename. Retargets take the form of "a.b:a.c" which will
* change all references to a.b to a.c.
*
* @param dottedNewName dotted name, e.g. com.foo.Bar
* @param classbytes the bytecode for the class to be renamed
* @param retargets retarget rules for references, of the form "a.b:b.a","c.d:d.c"
* @return bytecode for the modified class
*/
public static byte[] rename(String dottedNewName, byte[] classbytes, String... retargets) {
ClassReader fileReader = new ClassReader(classbytes);
RenameAdapter renameAdapter = new RenameAdapter(dottedNewName, retargets);
fileReader.accept(renameAdapter, 0);
byte[] renamed = renameAdapter.getBytes();
return renamed;
}
static class RenameAdapter extends ClassVisitor implements Opcodes {
private ClassWriter cw;
private String oldname;
private String newname;
private Map<String, String> retargets = new HashMap<String, String>();
public RenameAdapter(String newname, String[] retargets) {
super(ASM5, new ClassWriter(0));
cw = (ClassWriter) cv;
this.newname = newname.replace('.', '/');
if (retargets != null) {
for (String retarget : retargets) {
int i = retarget.indexOf(":");
this.retargets.put(retarget.substring(0, i).replace('.', '/'),
retarget.substring(i + 1).replace('.', '/'));
}
}
}
public byte[] getBytes() {
return cw.toByteArray();
}
private String retargetIfNecessary(String string) {
String value = retargets.get(string);
return value == null ? string : value;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
oldname = name;
if (superName != null) {
superName = retargetIfNecessary(superName);
}
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
interfaces[i] = retargetIfNecessary(interfaces[i]);
}
}
super.visit(version, access, newname, signature, superName, interfaces);
}
@Override
public void visitInnerClass(String name, String outername, String innerName, int access) {
super.visitInnerClass(renameRetargetIfNecessary(name), renameRetargetIfNecessary(outername),
renameRetargetIfNecessary(innerName), access);
}
private String renameRetargetIfNecessary(String string) {
String value = retargets.get(string);
if (value != null) {
return value;
}
if (string != null && string.indexOf(oldname) != -1) {
return string.replace(oldname, newname);
}
return string;
}
@Override
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
if (descriptor.indexOf(oldname) != -1) {
descriptor = descriptor.replace(oldname, newname);
}
else {
if (descriptor.indexOf(oldname) != -1) {
descriptor = descriptor.replace(oldname, newname);
}
for (String s : retargets.keySet()) {
if (descriptor.indexOf(s) != -1) {
descriptor = descriptor.replace(s, retargets.get(s));
}
}
}
MethodVisitor mv = super.visitMethod(flags, name, descriptor, signature, exceptions);
return new RenameMethodAdapter(mv, oldname, newname);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
}
else {
for (String s : retargets.keySet()) {
if (desc.indexOf(s) != -1) {
desc = desc.replace(s, retargets.get(s));
}
}
}
return super.visitField(access, name, desc, signature, value);
}
class RenameMethodAdapter extends MethodVisitor implements Opcodes {
String oldname;
String newname;
public RenameMethodAdapter(MethodVisitor mv, String oldname, String newname) {
super(ASM5, mv);
this.oldname = oldname;
this.newname = newname;
}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(oldname)) {
owner = newname;
}
else {
String retarget = retargets.get(owner);
if (retarget != null) {
owner = retarget;
}
}
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
}
else {
desc = checkIfShouldBeRewritten(desc);
}
mv.visitFieldInsn(opcode, owner, name, desc);
}
public void visitTypeInsn(int opcode, String type) {
if (type.equals(oldname)) {
type = newname;
}
else {
String retarget = retargets.get(type);
if (retarget != null) {
type = retarget;
}
else {
if (type.startsWith("[")) {
if (type.indexOf(oldname) != -1) {
type = type.replaceFirst(oldname, newname);
}
}
}
}
mv.visitTypeInsn(opcode, type);
}
@Override
public void visitLdcInsn(Object obj) {
// System.out.println("Possibly remapping "+obj);
if (obj instanceof Type) {
Type t = (Type) obj;
String s = t.getInternalName();
String retarget = retargets.get(s);
if (retarget != null) {
mv.visitLdcInsn(Type.getObjectType(retarget));
}
else {
mv.visitLdcInsn(obj);
}
}
else if (obj instanceof String) {
String s = (String) obj;
String retarget = retargets.get(s.replace('.', '/'));
if (retarget != null) {
mv.visitLdcInsn(retarget.replace('/', '.'));
}
else {
String oldnameDotted = oldname.replace('/', '.');
if (s.equals(oldnameDotted)) {
String nname = newname.replace('/', '.');
mv.visitLdcInsn(nname);
return;
}
else if (s.startsWith("[")) {
// might be array of oldname
if (s.indexOf(oldnameDotted) != -1) {
mv.visitLdcInsn(s.replaceFirst(oldnameDotted, newname.replace('/', '.')));
return;
}
}
mv.visitLdcInsn(obj);
}
}
else {
mv.visitLdcInsn(obj);
}
}
private String toString(Handle bsm) {
return "[" + bsm.getTag() + "]" + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
}
private String toString(Object[] os) {
StringBuilder buf = new StringBuilder();
if (os != null) {
buf.append("[");
for (int i = 0; i < os.length; i++) {
if (i > 0)
buf.append(",");
buf.append(os[i]);
}
buf.append("]");
}
else {
return "null";
}
return buf.toString();
}
private Handle retargetHandle(Handle oldHandle) {
int tag = oldHandle.getTag();
String owner = oldHandle.getOwner();
String name = oldHandle.getName();
String desc = oldHandle.getDesc();
// System.out.println("handle: owner: "+owner);
// System.out.println("handle: name: "+name);
// System.out.println("handle: desc: "+desc);
owner = renameRetargetIfNecessary(owner);
desc = renameRetargetIfNecessary(desc);
Handle newHandle = new Handle(tag, owner, name, desc);
return newHandle;
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
// Example:
// visitInvokeDynamicInsn(name=m,desc=()Lbasic/LambdaA2$Foo;,
// bsm=[6]java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
// bsmArgs=[()I,basic/LambdaA2.lambda$run$1()I (6),()I])
desc = renameRetargetIfNecessary(desc);
if (bsmArgs[1] instanceof Handle) {
bsmArgs[1] = retargetHandle((Handle) bsmArgs[1]);
}
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (owner.equals(oldname)) {
owner = newname;
}
else {
owner = retargetIfNecessary(owner);
}
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
}
else {
desc = checkIfShouldBeRewritten(desc);
}
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
private String checkIfShouldBeRewritten(String desc) {
for (String s : retargets.keySet()) {
if (desc.indexOf(s) != -1) {
desc = desc.replace(s, retargets.get(s));
}
}
return desc;
}
}
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
/**
* Modify a class by changing it from one name to another. References to other types can also be changed. Basically used
* in the test suite.
*
* @author Andy Clement
* @since 0.5.0
*/
public class ClassRenamer {
/**
* Rename a type - changing it to specified new name (which should be the dotted form of the name). Retargets are an
* optional sequence of retargets to also perform during the rename. Retargets take the form of "a.b:a.c" which will
* change all references to a.b to a.c.
*
* @param dottedNewName dotted name, e.g. com.foo.Bar
* @param classbytes the bytecode for the class to be renamed
* @param retargets retarget rules for references, of the form "a.b:b.a","c.d:d.c"
* @return bytecode for the modified class
*/
public static byte[] rename(String dottedNewName, byte[] classbytes, String... retargets) {
ClassReader fileReader = new ClassReader(classbytes);
RenameAdapter renameAdapter = new RenameAdapter(dottedNewName, retargets);
fileReader.accept(renameAdapter, 0);
byte[] renamed = renameAdapter.getBytes();
return renamed;
}
static class RenameAdapter extends ClassVisitor implements Opcodes {
private ClassWriter cw;
private String oldname;
private String newname;
private Map<String, String> retargets = new HashMap<String, String>();
public RenameAdapter(String newname, String[] retargets) {
super(ASM5, new ClassWriter(0));
cw = (ClassWriter) cv;
this.newname = newname.replace('.', '/');
if (retargets != null) {
for (String retarget : retargets) {
int i = retarget.indexOf(":");
this.retargets.put(retarget.substring(0, i).replace('.', '/'),
retarget.substring(i + 1).replace('.', '/'));
}
}
}
public byte[] getBytes() {
return cw.toByteArray();
}
private String retargetIfNecessary(String string) {
String value = retargets.get(string);
return value == null ? string : value;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
oldname = name;
if (superName != null) {
superName = retargetIfNecessary(superName);
}
if (interfaces != null) {
for (int i = 0; i < interfaces.length; i++) {
interfaces[i] = retargetIfNecessary(interfaces[i]);
}
}
super.visit(version, access, newname, signature, superName, interfaces);
}
@Override
public void visitInnerClass(String name, String outername, String innerName, int access) {
super.visitInnerClass(renameRetargetIfNecessary(name), renameRetargetIfNecessary(outername),
renameRetargetIfNecessary(innerName), access);
}
private String renameRetargetIfNecessary(String string) {
String value = retargets.get(string);
if (value != null) {
return value;
}
if (string != null && string.indexOf(oldname) != -1) {
return string.replace(oldname, newname);
}
return string;
}
@Override
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
if (descriptor.indexOf(oldname) != -1) {
descriptor = descriptor.replace(oldname, newname);
}
else {
if (descriptor.indexOf(oldname) != -1) {
descriptor = descriptor.replace(oldname, newname);
}
for (String s : retargets.keySet()) {
if (descriptor.indexOf(s) != -1) {
descriptor = descriptor.replace(s, retargets.get(s));
}
}
}
MethodVisitor mv = super.visitMethod(flags, name, descriptor, signature, exceptions);
return new RenameMethodAdapter(mv, oldname, newname);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
}
else {
for (String s : retargets.keySet()) {
if (desc.indexOf(s) != -1) {
desc = desc.replace(s, retargets.get(s));
}
}
}
return super.visitField(access, name, desc, signature, value);
}
class RenameMethodAdapter extends MethodVisitor implements Opcodes {
String oldname;
String newname;
public RenameMethodAdapter(MethodVisitor mv, String oldname, String newname) {
super(ASM5, mv);
this.oldname = oldname;
this.newname = newname;
}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(oldname)) {
owner = newname;
}
else {
String retarget = retargets.get(owner);
if (retarget != null) {
owner = retarget;
}
}
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
}
else {
desc = checkIfShouldBeRewritten(desc);
}
mv.visitFieldInsn(opcode, owner, name, desc);
}
public void visitTypeInsn(int opcode, String type) {
if (type.equals(oldname)) {
type = newname;
}
else {
String retarget = retargets.get(type);
if (retarget != null) {
type = retarget;
}
else {
if (type.startsWith("[")) {
if (type.indexOf(oldname) != -1) {
type = type.replaceFirst(oldname, newname);
}
}
}
}
mv.visitTypeInsn(opcode, type);
}
@Override
public void visitLdcInsn(Object obj) {
// System.out.println("Possibly remapping "+obj);
if (obj instanceof Type) {
Type t = (Type) obj;
String s = t.getInternalName();
String retarget = retargets.get(s);
if (retarget != null) {
mv.visitLdcInsn(Type.getObjectType(retarget));
}
else {
mv.visitLdcInsn(obj);
}
}
else if (obj instanceof String) {
String s = (String) obj;
String retarget = retargets.get(s.replace('.', '/'));
if (retarget != null) {
mv.visitLdcInsn(retarget.replace('/', '.'));
}
else {
String oldnameDotted = oldname.replace('/', '.');
if (s.equals(oldnameDotted)) {
String nname = newname.replace('/', '.');
mv.visitLdcInsn(nname);
return;
}
else if (s.startsWith("[")) {
// might be array of oldname
if (s.indexOf(oldnameDotted) != -1) {
mv.visitLdcInsn(s.replaceFirst(oldnameDotted, newname.replace('/', '.')));
return;
}
}
mv.visitLdcInsn(obj);
}
}
else {
mv.visitLdcInsn(obj);
}
}
private String toString(Handle bsm) {
return "[" + bsm.getTag() + "]" + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
}
private String toString(Object[] os) {
StringBuilder buf = new StringBuilder();
if (os != null) {
buf.append("[");
for (int i = 0; i < os.length; i++) {
if (i > 0)
buf.append(",");
buf.append(os[i]);
}
buf.append("]");
}
else {
return "null";
}
return buf.toString();
}
private Handle retargetHandle(Handle oldHandle) {
int tag = oldHandle.getTag();
String owner = oldHandle.getOwner();
String name = oldHandle.getName();
String desc = oldHandle.getDesc();
// System.out.println("handle: owner: "+owner);
// System.out.println("handle: name: "+name);
// System.out.println("handle: desc: "+desc);
owner = renameRetargetIfNecessary(owner);
desc = renameRetargetIfNecessary(desc);
Handle newHandle = new Handle(tag, owner, name, desc);
return newHandle;
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
// Example:
// visitInvokeDynamicInsn(name=m,desc=()Lbasic/LambdaA2$Foo;,
// bsm=[6]java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
// bsmArgs=[()I,basic/LambdaA2.lambda$run$1()I (6),()I])
desc = renameRetargetIfNecessary(desc);
if (bsmArgs[1] instanceof Handle) {
bsmArgs[1] = retargetHandle((Handle) bsmArgs[1]);
}
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (owner.equals(oldname)) {
owner = newname;
}
else {
owner = retargetIfNecessary(owner);
}
if (desc.indexOf(oldname) != -1) {
desc = desc.replace(oldname, newname);
}
else {
desc = checkIfShouldBeRewritten(desc);
}
mv.visitMethodInsn(opcode, owner, name, desc, itf);
}
private String checkIfShouldBeRewritten(String desc) {
for (String s : retargets.keySet()) {
if (desc.indexOf(s) != -1) {
desc = desc.replace(s, retargets.get(s));
}
}
return desc;
}
}
}
}

View File

@@ -1,260 +1,260 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
import org.objectweb.asm.Opcodes;
/**
* Common constants used throughout Spring Loaded.
*
* @author Andy Clement
* @since 0.5.0
*/
public interface Constants extends Opcodes {
public static final Integer DEFAULT_INT = Integer.valueOf(0);
public static final Byte DEFAULT_BYTE = Byte.valueOf((byte) 0);
public static final Character DEFAULT_CHAR = Character.valueOf((char) 0);
public static final Short DEFAULT_SHORT = Short.valueOf((short) 0);
public static final Long DEFAULT_LONG = Long.valueOf(0);
public static final Float DEFAULT_FLOAT = Float.valueOf(0);
public static final Double DEFAULT_DOUBLE = Double.valueOf(0);
public static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
static String magicDescriptorForGeneratedCtors = "org.springsource.loaded.C";
// TODO change r$ to _sl or sl throughout?
static String PREFIX = "r$";
static String tRegistryType = "org/springsource/loaded/TypeRegistry";
static String lRegistryType = "L" + tRegistryType + ";";
static String tDynamicallyDispatchable = "org/springsource/loaded/__DynamicallyDispatchable";
static String lDynamicallyDispatchable = "L" + tDynamicallyDispatchable + ";";
static String tReloadableType = "org/springsource/loaded/ReloadableType";
static String lReloadableType = "L" + tReloadableType + ";";
static String tInstanceStateManager = "org/springsource/loaded/ISMgr";
static String lInstanceStateManager = "L" + tInstanceStateManager + ";";
static String tStaticStateManager = "org/springsource/loaded/SSMgr";
static String lStaticStateManager = "L" + tStaticStateManager + ";";
static String fReloadableTypeFieldName = PREFIX + "type";
// Static field holding map and accessors
static String fStaticFieldsName = PREFIX + "sfields";
static String mStaticFieldSetterName = PREFIX + "sets";
static String mStaticFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)V";
static String mStaticFieldGetterName = PREFIX + "gets";
// Instance field holding map and accessors
static String fInstanceFieldsName = PREFIX + "fields";
static String mInstanceFieldSetterName = PREFIX + "set";
static String mInstanceFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V";
static String mInstanceFieldGetterName = PREFIX + "get";
static String mInstanceFieldGetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
static String mStaticFieldInterceptionRequired = "staticFieldInterceptionRequired";
static String mInstanceFieldInterceptionRequired = "instanceFieldInterceptionRequired";
// method called to see if the target of what is about to be called has changed
static String mChangedForInvocationName = "anyChanges";
static String mChangedForInvokeStaticName = "istcheck";
static String mChangedForInvokeInterfaceName = "iincheck";
static String mChangedForInvokeDynamicName = "idycheck";
static String mChangedForInvokeVirtualName = "ivicheck";
static String mChangedForInvokeSpecialName = "ispcheck";
static String mPerformInvokeDynamicName = "idyrun";
static String descriptorChangedForInvokeSpecialName = "(ILjava/lang/String;)Lorg/springsource/loaded/__DynamicallyDispatchable;";
static String mChangedForConstructorName = "ccheck";
static int WAS_INVOKESTATIC = 0x0001;
static int WAS_INVOKEVIRTUAL = 0x0002;
// Dynamic dispatch method
static String mDynamicDispatchName = "__execute";
static String mDynamicDispatchDescriptor = "([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
static String mInitializerName = "___init___";
static String mStaticInitializerName = "___clinit___";
static int ACC_PUBLIC_ABSTRACT = Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT;
static int ACC_PRIVATE_STATIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
static int ACC_PUBLIC_STATIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
static int ACC_PUBLIC_STATIC_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
static int ACC_PUBLIC_INTERFACE = Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT;
static int ACC_PUBLIC_STATIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PUBLIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PUBLIC_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
static int ACC_PUBLIC_PRIVATE_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
static int ACC_PRIVATE_STATIC_SYNTHETIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PRIVATE_PROTECTED = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
static int ACC_PRIVATE_STATIC_FINAL = ACC_FINAL | ACC_STATIC | ACC_PRIVATE;
static String[] NO_STRINGS = new String[0];
static Method[] NO_METHODS = new Method[0];
static Field[] NO_FIELDS = new Field[0];
//Name pattern used to recognise names of Executor classes.
static Pattern executorClassNamePattern = Pattern.compile("\\$\\$E[0-9,a-z,A-Z]+$");
static final String jlObject = "java/lang/Object";
//
public static int JLC_GETDECLAREDFIELDS = 0x0001;
public static int JLC_GETDECLAREDFIELD = 0x0002;
public static int JLC_GETFIELD = 0x0004;
public static int JLC_GETDECLAREDMETHODS = 0x0008;
public static int JLC_GETDECLAREDMETHOD = 0x0010;
public static int JLC_GETMETHOD = 0x0020;
public static int JLC_GETDECLAREDCONSTRUCTOR = 0x0040;
public static int JLC_GETMODIFIERS = 0x0080;
public static int JLC_GETMETHODS = 0x0100;
public static int JLC_GETCONSTRUCTOR = 0x0200;
public static int JLC_GETDECLAREDCONSTRUCTORS = 0x0400;
public static int JLRM_INVOKE = 0x0800;
public static int JLRF_GET = 0x1000;
public static int JLRF_GETLONG = 0x2000;
public static int JLOS_HASSTATICINITIALIZER = 0x4000;
// For rewritten reflection in system classes, these are used:
// The member names are used for fields *and* methods
static final String jlcgdfs = "__sljlcgdfs";
static final String jlcgdfsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Field;";
static final String jlcgdf = "__sljlcgdf";
static final String jlcgdfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
static final String jlcgf = "__sljlcgf";
static final String jlcgfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
static final String jlcgdms = "__sljlcgdms";
static final String jlcgdmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
static final String jlcgdm = "__sljlcgdm";
static final String jlcgdmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
static final String jlcgm = "__sljlcgm";
static final String jlcgmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
static final String jlcgdc = "__sljlcgdc";
static final String jlcgdcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
static final String jlcgc = "__sljlcgc";
static final String jlcgcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
static final String jlcgmods = "__sljlcgmods";
static final String jlcgmodsDescriptor = "(Ljava/lang/Class;)I";
static final String jlcgms = "__sljlcgms";
static final String jlcgmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
// TODO migrate those above to this slightly more comprehensible format
static final String jlcGetDeclaredConstructorsMember = "__sljlcgdcs";
static final String jlcGetDeclaredConstructorsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Constructor;";
static final String jlrmInvokeMember = "__sljlrmi";
static final String jlrmInvokeDescriptor = "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
static final String jlrfGetMember = "__sljlrfg";
static final String jlrfGetDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;";
static final String jlrfGetLongMember = "__sljlrfgl";
static final String jlrfGetLongDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J";
static final String jloObjectStream_hasInitializerMethod = "__sljlos_him";
static final String methodSuffixSuperDispatcher = "_$superdispatcher$";
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.regex.Pattern;
import org.objectweb.asm.Opcodes;
/**
* Common constants used throughout Spring Loaded.
*
* @author Andy Clement
* @since 0.5.0
*/
public interface Constants extends Opcodes {
public static final Integer DEFAULT_INT = Integer.valueOf(0);
public static final Byte DEFAULT_BYTE = Byte.valueOf((byte) 0);
public static final Character DEFAULT_CHAR = Character.valueOf((char) 0);
public static final Short DEFAULT_SHORT = Short.valueOf((short) 0);
public static final Long DEFAULT_LONG = Long.valueOf(0);
public static final Float DEFAULT_FLOAT = Float.valueOf(0);
public static final Double DEFAULT_DOUBLE = Double.valueOf(0);
public static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
static String magicDescriptorForGeneratedCtors = "org.springsource.loaded.C";
// TODO change r$ to _sl or sl throughout?
static String PREFIX = "r$";
static String tRegistryType = "org/springsource/loaded/TypeRegistry";
static String lRegistryType = "L" + tRegistryType + ";";
static String tDynamicallyDispatchable = "org/springsource/loaded/__DynamicallyDispatchable";
static String lDynamicallyDispatchable = "L" + tDynamicallyDispatchable + ";";
static String tReloadableType = "org/springsource/loaded/ReloadableType";
static String lReloadableType = "L" + tReloadableType + ";";
static String tInstanceStateManager = "org/springsource/loaded/ISMgr";
static String lInstanceStateManager = "L" + tInstanceStateManager + ";";
static String tStaticStateManager = "org/springsource/loaded/SSMgr";
static String lStaticStateManager = "L" + tStaticStateManager + ";";
static String fReloadableTypeFieldName = PREFIX + "type";
// Static field holding map and accessors
static String fStaticFieldsName = PREFIX + "sfields";
static String mStaticFieldSetterName = PREFIX + "sets";
static String mStaticFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)V";
static String mStaticFieldGetterName = PREFIX + "gets";
// Instance field holding map and accessors
static String fInstanceFieldsName = PREFIX + "fields";
static String mInstanceFieldSetterName = PREFIX + "set";
static String mInstanceFieldSetterDescriptor = "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V";
static String mInstanceFieldGetterName = PREFIX + "get";
static String mInstanceFieldGetterDescriptor = "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
static String mStaticFieldInterceptionRequired = "staticFieldInterceptionRequired";
static String mInstanceFieldInterceptionRequired = "instanceFieldInterceptionRequired";
// method called to see if the target of what is about to be called has changed
static String mChangedForInvocationName = "anyChanges";
static String mChangedForInvokeStaticName = "istcheck";
static String mChangedForInvokeInterfaceName = "iincheck";
static String mChangedForInvokeDynamicName = "idycheck";
static String mChangedForInvokeVirtualName = "ivicheck";
static String mChangedForInvokeSpecialName = "ispcheck";
static String mPerformInvokeDynamicName = "idyrun";
static String descriptorChangedForInvokeSpecialName = "(ILjava/lang/String;)Lorg/springsource/loaded/__DynamicallyDispatchable;";
static String mChangedForConstructorName = "ccheck";
static int WAS_INVOKESTATIC = 0x0001;
static int WAS_INVOKEVIRTUAL = 0x0002;
// Dynamic dispatch method
static String mDynamicDispatchName = "__execute";
static String mDynamicDispatchDescriptor = "([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
static String mInitializerName = "___init___";
static String mStaticInitializerName = "___clinit___";
static int ACC_PUBLIC_ABSTRACT = Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT;
static int ACC_PRIVATE_STATIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC;
static int ACC_PUBLIC_STATIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;
static int ACC_PUBLIC_STATIC_FINAL = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
static int ACC_PUBLIC_INTERFACE = Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT;
static int ACC_PUBLIC_STATIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PUBLIC_SYNTHETIC = Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PUBLIC_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
static int ACC_PUBLIC_PRIVATE_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
static int ACC_PRIVATE_STATIC_SYNTHETIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PRIVATE_PROTECTED = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
static int ACC_PRIVATE_STATIC_FINAL = ACC_FINAL | ACC_STATIC | ACC_PRIVATE;
static String[] NO_STRINGS = new String[0];
static Method[] NO_METHODS = new Method[0];
static Field[] NO_FIELDS = new Field[0];
//Name pattern used to recognise names of Executor classes.
static Pattern executorClassNamePattern = Pattern.compile("\\$\\$E[0-9,a-z,A-Z]+$");
static final String jlObject = "java/lang/Object";
//
public static int JLC_GETDECLAREDFIELDS = 0x0001;
public static int JLC_GETDECLAREDFIELD = 0x0002;
public static int JLC_GETFIELD = 0x0004;
public static int JLC_GETDECLAREDMETHODS = 0x0008;
public static int JLC_GETDECLAREDMETHOD = 0x0010;
public static int JLC_GETMETHOD = 0x0020;
public static int JLC_GETDECLAREDCONSTRUCTOR = 0x0040;
public static int JLC_GETMODIFIERS = 0x0080;
public static int JLC_GETMETHODS = 0x0100;
public static int JLC_GETCONSTRUCTOR = 0x0200;
public static int JLC_GETDECLAREDCONSTRUCTORS = 0x0400;
public static int JLRM_INVOKE = 0x0800;
public static int JLRF_GET = 0x1000;
public static int JLRF_GETLONG = 0x2000;
public static int JLOS_HASSTATICINITIALIZER = 0x4000;
// For rewritten reflection in system classes, these are used:
// The member names are used for fields *and* methods
static final String jlcgdfs = "__sljlcgdfs";
static final String jlcgdfsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Field;";
static final String jlcgdf = "__sljlcgdf";
static final String jlcgdfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
static final String jlcgf = "__sljlcgf";
static final String jlcgfDescriptor = "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Field;";
static final String jlcgdms = "__sljlcgdms";
static final String jlcgdmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
static final String jlcgdm = "__sljlcgdm";
static final String jlcgdmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
static final String jlcgm = "__sljlcgm";
static final String jlcgmDescriptor = "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;";
static final String jlcgdc = "__sljlcgdc";
static final String jlcgdcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
static final String jlcgc = "__sljlcgc";
static final String jlcgcDescriptor = "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;";
static final String jlcgmods = "__sljlcgmods";
static final String jlcgmodsDescriptor = "(Ljava/lang/Class;)I";
static final String jlcgms = "__sljlcgms";
static final String jlcgmsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Method;";
// TODO migrate those above to this slightly more comprehensible format
static final String jlcGetDeclaredConstructorsMember = "__sljlcgdcs";
static final String jlcGetDeclaredConstructorsDescriptor = "(Ljava/lang/Class;)[Ljava/lang/reflect/Constructor;";
static final String jlrmInvokeMember = "__sljlrmi";
static final String jlrmInvokeDescriptor = "(Ljava/lang/reflect/Method;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;";
static final String jlrfGetMember = "__sljlrfg";
static final String jlrfGetDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)Ljava/lang/Object;";
static final String jlrfGetLongMember = "__sljlrfgl";
static final String jlrfGetLongDescriptor = "(Ljava/lang/reflect/Field;Ljava/lang/Object;)J";
static final String jloObjectStream_hasInitializerMethod = "__sljlos_him";
static final String methodSuffixSuperDispatcher = "_$superdispatcher$";
}

View File

@@ -1,317 +1,317 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodNode;
/**
* Captures the information about the reloaded parts of a type that vary each time a new version is loaded.
*
* @author Andy Clement
* @since 0.5.0
*/
public class CurrentLiveVersion {
private static Logger log = Logger.getLogger(CurrentLiveVersion.class.getName());
// Which reloadable type this represents the live version of
final ReloadableType reloadableType;
// Type descriptor for this live version
final TypeDescriptor typeDescriptor;
// 'stamp' (i.e. suffix) for this version
final String versionstamp;
public final IncrementalTypeDescriptor incrementalTypeDescriptor;
String dispatcherName;
byte[] dispatcher;
Class<?> dispatcherClass;
Object dispatcherInstance;
String executorName;
byte[] executor;
Class<?> executorClass;
TypeDelta typeDelta;
private Method staticInitializer;
private boolean haveLookedForStaticInitializer;
public boolean staticInitializedNeedsRerunningOnDefine = false;
public CurrentLiveVersion(ReloadableType reloadableType, String versionstamp, byte[] newbytedata) {
if (GlobalConfiguration.logging && log.isLoggable(Level.FINER)) {
log.entering("CurrentLiveVersion", "<init>", " new version of " + reloadableType.getName()
+ " loaded, version stamp '"
+ versionstamp + "'");
}
this.reloadableType = reloadableType;
this.typeDescriptor = reloadableType.getTypeRegistry().getExtractor().extract(newbytedata, true);
this.versionstamp = versionstamp;
if (GlobalConfiguration.assertsMode) {
if (!this.typeDescriptor.getName().equals(reloadableType.typedescriptor.getName())) {
throw new IllegalStateException("New version has wrong name. Expected "
+ reloadableType.typedescriptor.getName()
+ " but was " + typeDescriptor.getName());
}
}
newbytedata = GlobalConfiguration.callsideRewritingOn ? MethodInvokerRewriter.rewrite(
reloadableType.typeRegistry,
newbytedata) : newbytedata;
this.incrementalTypeDescriptor = new IncrementalTypeDescriptor(reloadableType.typedescriptor);
this.incrementalTypeDescriptor.setLatestTypeDescriptor(this.typeDescriptor);
// Executors for interfaces simply hold annotations
this.executor = reloadableType.getTypeRegistry().executorBuilder.createFor(reloadableType, versionstamp,
typeDescriptor,
newbytedata);
if (GlobalConfiguration.classesToDump != null
&& GlobalConfiguration.classesToDump.contains(reloadableType.getSlashedName())) {
Utils.dump(Utils.getExecutorName(reloadableType.getName(), versionstamp).replace('.', '/'), this.executor);
}
if (!typeDescriptor.isInterface()) {
this.dispatcherName = Utils.getDispatcherName(reloadableType.getName(), versionstamp);
this.executorName = Utils.getExecutorName(reloadableType.getName(), versionstamp);
this.dispatcher = DispatcherBuilder.createFor(reloadableType, incrementalTypeDescriptor, versionstamp);
}
reloadableType.typeRegistry.checkChildClassLoader(reloadableType);
define();
}
/**
* Defines this version. Called up front but can also be called later if the ChildClassLoader in a type registry is
* discarded and recreated.
*/
public void define() {
staticInitializer = null;
haveLookedForStaticInitializer = false;
if (!typeDescriptor.isInterface()) {
try {
dispatcherClass = reloadableType.typeRegistry.defineClass(dispatcherName, dispatcher, false);
}
catch (RuntimeException t) {
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
if (t.getMessage().indexOf("duplicate class definition") == -1) {
throw t;
}
else {
t.printStackTrace();
}
}
}
try {
executorClass = reloadableType.typeRegistry.defineClass(executorName, executor, false);
}
catch (RuntimeException t) {
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
if (t.getMessage().indexOf("duplicate class definition") == -1) {
throw t;
}
else {
t.printStackTrace();
}
}
if (!typeDescriptor.isInterface()) {
try {
dispatcherInstance = dispatcherClass.newInstance();
}
catch (InstantiationException e) {
throw new RuntimeException("Unable to build dispatcher class instance", e);
}
catch (IllegalAccessException e) {
throw new RuntimeException("Unable to build dispatcher class instance", e);
}
}
}
public MethodMember getReloadableMethod(String name, String descriptor) {
// Look through the methods on the latest loaded version and find the method we want
MethodMember[] methods = incrementalTypeDescriptor.getLatestTypeDescriptor().getMethods();
for (MethodMember rmethod : methods) {
if (rmethod.getName().equals(name)) {
if (descriptor.equals(rmethod.getDescriptor())) {
return rmethod;
}
}
}
return null;
}
// TODO should be caching the result in the MethodMember objects for speed
public Method getExecutorMethod(MethodMember methodMember) {
String executorDescriptor;
String name;
//What to search for:
if (methodMember.isConstructor()) {
name = Constants.mInitializerName;
}
else {
name = methodMember.getName();
}
executorDescriptor = getExecutorDescriptor(methodMember);
//Search for it:
if (executorClass != null) {
Method[] executorMethods = executorClass.getDeclaredMethods();
for (Method executor : executorMethods) {
if (executor.getName().equals(name) && Type.getMethodDescriptor(executor).equals(executorDescriptor)) {
return executor;
}
}
}
return null;
}
private String getExecutorDescriptor(MethodMember methodMember) {
Type[] params = Type.getArgumentTypes(methodMember.getDescriptor());
Type[] newParametersArray = params;
if (!methodMember.isStatic()) {
newParametersArray = new Type[params.length + 1];
System.arraycopy(params, 0, newParametersArray, 1, params.length);
newParametersArray[0] = Type.getType(reloadableType.getClazz());
}
String executorDescriptor = Type.getMethodDescriptor(Type.getReturnType(methodMember.getDescriptor()),
newParametersArray);
return executorDescriptor;
}
@Override
public String toString() {
return "CurrentLiveVersion [reloadableType=" + reloadableType + ", typeDescriptor=" + typeDescriptor
+ ", versionstamp="
+ versionstamp + ", dispatcherName=" + dispatcherName + ", executorName=" + executorName + "]";
}
public Class<?> getExecutorClass() {
return executorClass;
}
public String getVersionStamp() {
return versionstamp;
}
public Field getExecutorField(String name) throws SecurityException, NoSuchFieldException {
return executorClass.getDeclaredField(name);
}
public TypeDelta getTypeDelta() {
return typeDelta;
}
public void setTypeDelta(TypeDelta td) {
typeDelta = td;
}
public boolean hasClinit() {
return typeDescriptor.hasClinit();
}
public boolean hasConstructorChanged(String descriptor) {
MethodMember mm = typeDescriptor.getConstructor(descriptor);
return hasConstructorChanged(mm);
}
public boolean hasConstructorChanged(MethodMember mm) {
if (mm == null) {
return true;
}
// need to look at the delta
if (typeDelta.haveMethodsChangedOrBeenAddedOrRemoved()) {
if (typeDelta.haveMethodsChanged()) {
MethodDelta md = typeDelta.changedMethods.get(mm.name + mm.descriptor);
if (md != null) {
return true;
}
}
if (typeDelta.haveMethodsBeenAdded()) {
MethodNode mn = typeDelta.brandNewMethods.get(mm.name + mm.descriptor);
if (mn != null) {
return true;
}
}
if (typeDelta.haveMethodsBeenDeleted()) {
MethodNode mn = typeDelta.lostMethods.get(mm.name + mm.descriptor);
if (mn != null) {
return true;
}
}
}
return false;
}
// TODO can we speed this up?
public boolean hasConstructorChanged(int ctorId) {
// need to find the constructor that id is for
MethodMember mm = typeDescriptor.getConstructor(ctorId);
return hasConstructorChanged(mm);
}
public void clearClassloaderLinks() {
this.executorClass = null;
this.dispatcherClass = null;
}
public void reloadMostRecentDispatcherAndExecutor() {
define();
}
public Object getDispatcherInstance() {
// TODO Auto-generated method stub
return null;
}
public void runStaticInitializer() {
if (!haveLookedForStaticInitializer) {
try {
staticInitializer = this.getExecutorClass().getDeclaredMethod(Constants.mStaticInitializerName);
}
catch (NoSuchMethodException e) {
// some types don't have a static initializer, that is OK
}
haveLookedForStaticInitializer = true;
}
if (staticInitializer != null) {
try {
staticInitializer.invoke(null);
}
catch (Exception e) {
log.severe("Unexpected exception whilst trying to call the static initializer on "
+ this.reloadableType.getName());
e.printStackTrace(); // TODO remove when happy
}
}
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodNode;
/**
* Captures the information about the reloaded parts of a type that vary each time a new version is loaded.
*
* @author Andy Clement
* @since 0.5.0
*/
public class CurrentLiveVersion {
private static Logger log = Logger.getLogger(CurrentLiveVersion.class.getName());
// Which reloadable type this represents the live version of
final ReloadableType reloadableType;
// Type descriptor for this live version
final TypeDescriptor typeDescriptor;
// 'stamp' (i.e. suffix) for this version
final String versionstamp;
public final IncrementalTypeDescriptor incrementalTypeDescriptor;
String dispatcherName;
byte[] dispatcher;
Class<?> dispatcherClass;
Object dispatcherInstance;
String executorName;
byte[] executor;
Class<?> executorClass;
TypeDelta typeDelta;
private Method staticInitializer;
private boolean haveLookedForStaticInitializer;
public boolean staticInitializedNeedsRerunningOnDefine = false;
public CurrentLiveVersion(ReloadableType reloadableType, String versionstamp, byte[] newbytedata) {
if (GlobalConfiguration.logging && log.isLoggable(Level.FINER)) {
log.entering("CurrentLiveVersion", "<init>", " new version of " + reloadableType.getName()
+ " loaded, version stamp '"
+ versionstamp + "'");
}
this.reloadableType = reloadableType;
this.typeDescriptor = reloadableType.getTypeRegistry().getExtractor().extract(newbytedata, true);
this.versionstamp = versionstamp;
if (GlobalConfiguration.assertsMode) {
if (!this.typeDescriptor.getName().equals(reloadableType.typedescriptor.getName())) {
throw new IllegalStateException("New version has wrong name. Expected "
+ reloadableType.typedescriptor.getName()
+ " but was " + typeDescriptor.getName());
}
}
newbytedata = GlobalConfiguration.callsideRewritingOn ? MethodInvokerRewriter.rewrite(
reloadableType.typeRegistry,
newbytedata) : newbytedata;
this.incrementalTypeDescriptor = new IncrementalTypeDescriptor(reloadableType.typedescriptor);
this.incrementalTypeDescriptor.setLatestTypeDescriptor(this.typeDescriptor);
// Executors for interfaces simply hold annotations
this.executor = reloadableType.getTypeRegistry().executorBuilder.createFor(reloadableType, versionstamp,
typeDescriptor,
newbytedata);
if (GlobalConfiguration.classesToDump != null
&& GlobalConfiguration.classesToDump.contains(reloadableType.getSlashedName())) {
Utils.dump(Utils.getExecutorName(reloadableType.getName(), versionstamp).replace('.', '/'), this.executor);
}
if (!typeDescriptor.isInterface()) {
this.dispatcherName = Utils.getDispatcherName(reloadableType.getName(), versionstamp);
this.executorName = Utils.getExecutorName(reloadableType.getName(), versionstamp);
this.dispatcher = DispatcherBuilder.createFor(reloadableType, incrementalTypeDescriptor, versionstamp);
}
reloadableType.typeRegistry.checkChildClassLoader(reloadableType);
define();
}
/**
* Defines this version. Called up front but can also be called later if the ChildClassLoader in a type registry is
* discarded and recreated.
*/
public void define() {
staticInitializer = null;
haveLookedForStaticInitializer = false;
if (!typeDescriptor.isInterface()) {
try {
dispatcherClass = reloadableType.typeRegistry.defineClass(dispatcherName, dispatcher, false);
}
catch (RuntimeException t) {
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
if (t.getMessage().indexOf("duplicate class definition") == -1) {
throw t;
}
else {
t.printStackTrace();
}
}
}
try {
executorClass = reloadableType.typeRegistry.defineClass(executorName, executor, false);
}
catch (RuntimeException t) {
// TODO check for something strange. something to do with the file detection misbehaving, see the same file attempted to be reloaded twice...
if (t.getMessage().indexOf("duplicate class definition") == -1) {
throw t;
}
else {
t.printStackTrace();
}
}
if (!typeDescriptor.isInterface()) {
try {
dispatcherInstance = dispatcherClass.newInstance();
}
catch (InstantiationException e) {
throw new RuntimeException("Unable to build dispatcher class instance", e);
}
catch (IllegalAccessException e) {
throw new RuntimeException("Unable to build dispatcher class instance", e);
}
}
}
public MethodMember getReloadableMethod(String name, String descriptor) {
// Look through the methods on the latest loaded version and find the method we want
MethodMember[] methods = incrementalTypeDescriptor.getLatestTypeDescriptor().getMethods();
for (MethodMember rmethod : methods) {
if (rmethod.getName().equals(name)) {
if (descriptor.equals(rmethod.getDescriptor())) {
return rmethod;
}
}
}
return null;
}
// TODO should be caching the result in the MethodMember objects for speed
public Method getExecutorMethod(MethodMember methodMember) {
String executorDescriptor;
String name;
//What to search for:
if (methodMember.isConstructor()) {
name = Constants.mInitializerName;
}
else {
name = methodMember.getName();
}
executorDescriptor = getExecutorDescriptor(methodMember);
//Search for it:
if (executorClass != null) {
Method[] executorMethods = executorClass.getDeclaredMethods();
for (Method executor : executorMethods) {
if (executor.getName().equals(name) && Type.getMethodDescriptor(executor).equals(executorDescriptor)) {
return executor;
}
}
}
return null;
}
private String getExecutorDescriptor(MethodMember methodMember) {
Type[] params = Type.getArgumentTypes(methodMember.getDescriptor());
Type[] newParametersArray = params;
if (!methodMember.isStatic()) {
newParametersArray = new Type[params.length + 1];
System.arraycopy(params, 0, newParametersArray, 1, params.length);
newParametersArray[0] = Type.getType(reloadableType.getClazz());
}
String executorDescriptor = Type.getMethodDescriptor(Type.getReturnType(methodMember.getDescriptor()),
newParametersArray);
return executorDescriptor;
}
@Override
public String toString() {
return "CurrentLiveVersion [reloadableType=" + reloadableType + ", typeDescriptor=" + typeDescriptor
+ ", versionstamp="
+ versionstamp + ", dispatcherName=" + dispatcherName + ", executorName=" + executorName + "]";
}
public Class<?> getExecutorClass() {
return executorClass;
}
public String getVersionStamp() {
return versionstamp;
}
public Field getExecutorField(String name) throws SecurityException, NoSuchFieldException {
return executorClass.getDeclaredField(name);
}
public TypeDelta getTypeDelta() {
return typeDelta;
}
public void setTypeDelta(TypeDelta td) {
typeDelta = td;
}
public boolean hasClinit() {
return typeDescriptor.hasClinit();
}
public boolean hasConstructorChanged(String descriptor) {
MethodMember mm = typeDescriptor.getConstructor(descriptor);
return hasConstructorChanged(mm);
}
public boolean hasConstructorChanged(MethodMember mm) {
if (mm == null) {
return true;
}
// need to look at the delta
if (typeDelta.haveMethodsChangedOrBeenAddedOrRemoved()) {
if (typeDelta.haveMethodsChanged()) {
MethodDelta md = typeDelta.changedMethods.get(mm.name + mm.descriptor);
if (md != null) {
return true;
}
}
if (typeDelta.haveMethodsBeenAdded()) {
MethodNode mn = typeDelta.brandNewMethods.get(mm.name + mm.descriptor);
if (mn != null) {
return true;
}
}
if (typeDelta.haveMethodsBeenDeleted()) {
MethodNode mn = typeDelta.lostMethods.get(mm.name + mm.descriptor);
if (mn != null) {
return true;
}
}
}
return false;
}
// TODO can we speed this up?
public boolean hasConstructorChanged(int ctorId) {
// need to find the constructor that id is for
MethodMember mm = typeDescriptor.getConstructor(ctorId);
return hasConstructorChanged(mm);
}
public void clearClassloaderLinks() {
this.executorClass = null;
this.dispatcherClass = null;
}
public void reloadMostRecentDispatcherAndExecutor() {
define();
}
public Object getDispatcherInstance() {
// TODO Auto-generated method stub
return null;
}
public void runStaticInitializer() {
if (!haveLookedForStaticInitializer) {
try {
staticInitializer = this.getExecutorClass().getDeclaredMethod(Constants.mStaticInitializerName);
}
catch (NoSuchMethodException e) {
// some types don't have a static initializer, that is OK
}
haveLookedForStaticInitializer = true;
}
if (staticInitializer != null) {
try {
staticInitializer.invoke(null);
}
catch (Exception e) {
log.severe("Unexpected exception whilst trying to call the static initializer on "
+ this.reloadableType.getName());
e.printStackTrace(); // TODO remove when happy
}
}
}
}

View File

@@ -1,348 +1,348 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils.ReturnType;
/**
* Builder that creates the dispatcher. The dispatcher is the implementation of the interface extracted for a type which
* then delegates to the executor. A new dispatcher (and executor) is built for each class reload.
*
* @author Andy Clement
* @since 0.5.0
*/
public class DispatcherBuilder {
/**
* Factory method that builds the dispatcher for a specified reloadabletype.
*
* @param rtype the reloadable type
* @param newVersionTypeDescriptor the descriptor of the new version (the executor will be generated according to
* this)
* @param versionstamp the suffix that should be appended to the generated dispatcher
* @return the bytecode for the new dispatcher
*/
public static byte[] createFor(ReloadableType rtype, IncrementalTypeDescriptor newVersionTypeDescriptor,
String versionstamp) {
ClassReader fileReader = new ClassReader(rtype.interfaceBytes);
DispatcherBuilderVisitor dispatcherVisitor = new DispatcherBuilderVisitor(rtype, newVersionTypeDescriptor,
versionstamp);
fileReader.accept(dispatcherVisitor, 0);
return dispatcherVisitor.getBytes();
}
/**
* Whilst visiting the interface, the implementation is created.
*/
static class DispatcherBuilderVisitor extends ClassVisitor implements Opcodes, Constants {
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
private String classname;
private String executorClassName;
private String suffix;
private ReloadableType rtype;
private IncrementalTypeDescriptor typeDescriptor;
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
super(ASM5);
this.classname = rtype.getSlashedName();
this.typeDescriptor = typeDescriptor;
this.suffix = suffix;
this.rtype = rtype;
this.executorClassName = Utils.getExecutorName(classname, suffix);
}
public byte[] getBytes() {
return cw.toByteArray();
}
public void visit(int version, int flags, String name, String signature, String superclassName,
String[] interfaceNames) {
String dispatcherName = Utils.getDispatcherName(classname, suffix);
cw.visit(version, Opcodes.ACC_PUBLIC, dispatcherName, null, "java/lang/Object",
new String[] { Utils.getInterfaceName(classname),
"org/springsource/loaded/__DynamicallyDispatchable" });
generateDefaultConstructor();
}
private void generateDefaultConstructor() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
private void generateClinitDispatcher() {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, mStaticInitializerName, "()V", null, null);
mv.visitCode();
mv.visitMethodInsn(INVOKESTATIC, executorClassName, mStaticInitializerName, "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
return null;
}
public void visitAttribute(Attribute arg0) {
}
public void visitEnd() {
}
public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4) {
return null;
}
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
if (name.equals(mDynamicDispatchName)) {
generateDynamicDispatchMethod(name, descriptor, signature, exceptions);
}
else if (!name.equals("<init>")) {
generateRegularMethod(name, descriptor, signature, exceptions);
}
return null;
}
/**
* Generate the body of the dynamic dispatcher method. This method is responsible for calling all the methods
* that are added to a type after the first time it is defined.
*/
private void generateDynamicDispatchMethod(String name, String descriptor, String signature, String[] exceptions) {
final int indexDispatcherInstance = 0;
final int indexArgs = 1;
final int indexTarget = 2;
final int indexNameAndDescriptor = 3;
// Should be generating the code for each additional method in
// the executor (new version) that wasn't in the original.
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, name, descriptor, signature, exceptions);
mv.visitCode();
// Entries required here for all methods that exist in the new version but didn't exist in the original version
// There should be no entries for catchers
int maxStack = 0;
// Basically generate a long if..else sequence for each method
List<MethodMember> methods = new ArrayList<MethodMember>(typeDescriptor.getNewOrChangedMethods());
// these are added because we may be calling through the dynamic dispatcher if calling from an invokeinterface - the invokeinterface
// will call __execute on the interface, which is then implemented by the real class - but it may be that the
// actual type implementing the interface already implements that method - if the dispatcher doesn't recognize
// it then we may go bang
// System.out.println("Generating __execute in type " + classname);
for (MethodMember m : typeDescriptor.getOriginal().getMethods()) {
methods.add(m);
}
for (MethodMember method : methods) {
if (MethodMember.isCatcher(method) || MethodMember.isSuperDispatcher(method)) { // for reason above, may also need to consider catchers here - what if an interface is changed to add a toString() method, for example
continue;
// would the implementation for a catcher call the super catcher?
}
// System.out.println("Generating handler for " + method.name);
String nameWithDescriptor = new StringBuilder(method.name).append(method.descriptor).toString();
// 2. Load the input name+descriptor and compare it with this method:
mv.visitVarInsn(ALOAD, 3);
mv.visitLdcInsn(nameWithDescriptor);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label label = new Label();
mv.visitJumpInsn(IFEQ, label); // means if false
// 3. Generate the code that will call the method on the executor:
if (!method.isStatic()) {
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, classname);
}
String callDescriptor = method.isStatic() ? method.descriptor : Utils.insertExtraParameter(classname,
method.descriptor);
int pcount = Utils.getParameterCount(method.descriptor);
if (pcount > maxStack) {
pcount = maxStack;
}
// 4. Unpack parameter array to fit the descriptor for that method
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, method.descriptor, 1);
ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, method.name, callDescriptor, false);
if (returnType.isVoid()) {
mv.visitInsn(ACONST_NULL);
}
else if (returnType.isPrimitive()) {
Utils.insertBoxInsns(mv, returnType.descriptor);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitLabel(label);
}
for (MethodMember ctor : typeDescriptor.getLatestTypeDescriptor().getConstructors()) {
String nameWithDescriptor = new StringBuilder(ctor.name).append(ctor.descriptor).toString();
// 2. Load the input name+descriptor and compare it with this method:
// if (nameAndDescriptor.equals(xxx)) {
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
mv.visitLdcInsn(nameWithDescriptor);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label label = new Label();
mv.visitJumpInsn(IFEQ, label); // means if false
// 3. Generate the code that will call the method on the executor:
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, classname);
String callDescriptor = Utils.insertExtraParameter(classname, ctor.descriptor);
int pcount = Utils.getParameterCount(ctor.descriptor);
if (pcount > maxStack) {
pcount = maxStack;
}
// 4. Unpack parameter array to fit the descriptor for that method
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, ctor.descriptor, 1);
// ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, "___init___", callDescriptor, false);
// if (returnType.isVoid()) {
mv.visitInsn(ACONST_NULL);
// } else if (returnType.isPrimitive()) {
// Utils.insertBoxInsns(mv, returnType.descriptor);
// }
mv.visitInsn(Opcodes.ARETURN);
mv.visitLabel(label);
}
// 5. Throw exception as dynamic dispatcher has been called for something it shouldn't have
// At this point we failed to find it as a method we can dispatch to our executor, so we want
// to pass it 'up' to our supertype. We need to get the dispatcher for our superclass
// and then call the __execute() on it, assuming that it will be able to handle this request.
// alternative 1: use the dispatcher for the superclass
// Determine the supertype
String slashedSupertypeName = rtype.getTypeDescriptor().getSupertypeName();
// getDispatcher will give us the dispatcher for the supertype
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedSupertypeName, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "getDispatcher",
"()Lorg/springsource/loaded/__DynamicallyDispatchable;", false);
// alternative 2: find the right dispatcher - i.e. who in the super hierarchy provides that nameAndDescriptor
// now invoke the dynamic dispatch call on that dispatcher
mv.visitVarInsn(ALOAD, indexArgs);
mv.visitVarInsn(ALOAD, indexTarget);
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
mv.visitMethodInsn(INVOKEINTERFACE, tDynamicallyDispatchable, mDynamicDispatchName,
mDynamicDispatchDescriptor, false);
mv.visitInsn(ARETURN);
// mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
// mv.visitInsn(DUP);
// mv.visitVarInsn(ALOAD, 3);
// mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V");
// mv.visitInsn(ATHROW);
mv.visitMaxs(maxStack, 6);
mv.visitEnd();
}
/**
* Called to generate the implementation of a normal method on the interface - a normal method is one that did
* exist when the type was first defined. Might be a catcher.
*/
private void generateRegularMethod(String name, String descriptor, String signature, String[] exceptions) {
// The original descriptor is how it was defined on the original type and how it is defined in the executor class.
// The original descriptor is this descriptor with the first parameter trimmed off.
boolean isClinit = name.equals("___clinit___");
String originalDescriptor = isClinit ? descriptor : Utils.stripFirstParameter(descriptor);
MethodMember method = null;
// Detect if the name has been modified for clash avoidance reasons
if (name.equals("___init___")) {
// it is a ctor
method = rtype.getConstructor(originalDescriptor);
}
else {
if (isClinit) {
generateClinitDispatcher();
return;
}
else {
// TODO need a better solution that these __
if (name.startsWith("__") && !name.equals("__$swapInit")) { // __$swapInit is the groovy reset method
// clash avoidance name
method = rtype.getMethod(name.substring(2), originalDescriptor);
}
else {
method = rtype.getMethod(name, originalDescriptor);
}
}
}
boolean isStatic = method.isStatic();
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name, descriptor, signature,
exceptions);
mv.visitCode();
// The input descriptor will include the extra initial parameter (the instance, or null for static methods)
ReturnType returnTypeDescriptor = Utils.getReturnTypeDescriptor(descriptor);
// For a static method the first parameter can be ignored
int params = Utils.getParameterCount(descriptor);
String callDescriptor = isStatic ? originalDescriptor : descriptor;
Utils.createLoadsBasedOnDescriptor(mv, callDescriptor, isStatic ? 2 : 1);
mv.visitMethodInsn(INVOKESTATIC, executorClassName, name, callDescriptor, false);
Utils.addCorrectReturnInstruction(mv, returnTypeDescriptor, false);
mv.visitMaxs(params, params + 1);
mv.visitEnd();
}
public void visitOuterClass(String arg0, String arg1, String arg2) {
}
public void visitSource(String arg0, String arg1) {
}
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils.ReturnType;
/**
* Builder that creates the dispatcher. The dispatcher is the implementation of the interface extracted for a type which
* then delegates to the executor. A new dispatcher (and executor) is built for each class reload.
*
* @author Andy Clement
* @since 0.5.0
*/
public class DispatcherBuilder {
/**
* Factory method that builds the dispatcher for a specified reloadabletype.
*
* @param rtype the reloadable type
* @param newVersionTypeDescriptor the descriptor of the new version (the executor will be generated according to
* this)
* @param versionstamp the suffix that should be appended to the generated dispatcher
* @return the bytecode for the new dispatcher
*/
public static byte[] createFor(ReloadableType rtype, IncrementalTypeDescriptor newVersionTypeDescriptor,
String versionstamp) {
ClassReader fileReader = new ClassReader(rtype.interfaceBytes);
DispatcherBuilderVisitor dispatcherVisitor = new DispatcherBuilderVisitor(rtype, newVersionTypeDescriptor,
versionstamp);
fileReader.accept(dispatcherVisitor, 0);
return dispatcherVisitor.getBytes();
}
/**
* Whilst visiting the interface, the implementation is created.
*/
static class DispatcherBuilderVisitor extends ClassVisitor implements Opcodes, Constants {
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
private String classname;
private String executorClassName;
private String suffix;
private ReloadableType rtype;
private IncrementalTypeDescriptor typeDescriptor;
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
super(ASM5);
this.classname = rtype.getSlashedName();
this.typeDescriptor = typeDescriptor;
this.suffix = suffix;
this.rtype = rtype;
this.executorClassName = Utils.getExecutorName(classname, suffix);
}
public byte[] getBytes() {
return cw.toByteArray();
}
public void visit(int version, int flags, String name, String signature, String superclassName,
String[] interfaceNames) {
String dispatcherName = Utils.getDispatcherName(classname, suffix);
cw.visit(version, Opcodes.ACC_PUBLIC, dispatcherName, null, "java/lang/Object",
new String[] { Utils.getInterfaceName(classname),
"org/springsource/loaded/__DynamicallyDispatchable" });
generateDefaultConstructor();
}
private void generateDefaultConstructor() {
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
private void generateClinitDispatcher() {
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, mStaticInitializerName, "()V", null, null);
mv.visitCode();
mv.visitMethodInsn(INVOKESTATIC, executorClassName, mStaticInitializerName, "()V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
public AnnotationVisitor visitAnnotation(String arg0, boolean arg1) {
return null;
}
public void visitAttribute(Attribute arg0) {
}
public void visitEnd() {
}
public FieldVisitor visitField(int arg0, String arg1, String arg2, String arg3, Object arg4) {
return null;
}
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
if (name.equals(mDynamicDispatchName)) {
generateDynamicDispatchMethod(name, descriptor, signature, exceptions);
}
else if (!name.equals("<init>")) {
generateRegularMethod(name, descriptor, signature, exceptions);
}
return null;
}
/**
* Generate the body of the dynamic dispatcher method. This method is responsible for calling all the methods
* that are added to a type after the first time it is defined.
*/
private void generateDynamicDispatchMethod(String name, String descriptor, String signature, String[] exceptions) {
final int indexDispatcherInstance = 0;
final int indexArgs = 1;
final int indexTarget = 2;
final int indexNameAndDescriptor = 3;
// Should be generating the code for each additional method in
// the executor (new version) that wasn't in the original.
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, name, descriptor, signature, exceptions);
mv.visitCode();
// Entries required here for all methods that exist in the new version but didn't exist in the original version
// There should be no entries for catchers
int maxStack = 0;
// Basically generate a long if..else sequence for each method
List<MethodMember> methods = new ArrayList<MethodMember>(typeDescriptor.getNewOrChangedMethods());
// these are added because we may be calling through the dynamic dispatcher if calling from an invokeinterface - the invokeinterface
// will call __execute on the interface, which is then implemented by the real class - but it may be that the
// actual type implementing the interface already implements that method - if the dispatcher doesn't recognize
// it then we may go bang
// System.out.println("Generating __execute in type " + classname);
for (MethodMember m : typeDescriptor.getOriginal().getMethods()) {
methods.add(m);
}
for (MethodMember method : methods) {
if (MethodMember.isCatcher(method) || MethodMember.isSuperDispatcher(method)) { // for reason above, may also need to consider catchers here - what if an interface is changed to add a toString() method, for example
continue;
// would the implementation for a catcher call the super catcher?
}
// System.out.println("Generating handler for " + method.name);
String nameWithDescriptor = new StringBuilder(method.name).append(method.descriptor).toString();
// 2. Load the input name+descriptor and compare it with this method:
mv.visitVarInsn(ALOAD, 3);
mv.visitLdcInsn(nameWithDescriptor);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label label = new Label();
mv.visitJumpInsn(IFEQ, label); // means if false
// 3. Generate the code that will call the method on the executor:
if (!method.isStatic()) {
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, classname);
}
String callDescriptor = method.isStatic() ? method.descriptor : Utils.insertExtraParameter(classname,
method.descriptor);
int pcount = Utils.getParameterCount(method.descriptor);
if (pcount > maxStack) {
pcount = maxStack;
}
// 4. Unpack parameter array to fit the descriptor for that method
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, method.descriptor, 1);
ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, method.name, callDescriptor, false);
if (returnType.isVoid()) {
mv.visitInsn(ACONST_NULL);
}
else if (returnType.isPrimitive()) {
Utils.insertBoxInsns(mv, returnType.descriptor);
}
mv.visitInsn(Opcodes.ARETURN);
mv.visitLabel(label);
}
for (MethodMember ctor : typeDescriptor.getLatestTypeDescriptor().getConstructors()) {
String nameWithDescriptor = new StringBuilder(ctor.name).append(ctor.descriptor).toString();
// 2. Load the input name+descriptor and compare it with this method:
// if (nameAndDescriptor.equals(xxx)) {
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
mv.visitLdcInsn(nameWithDescriptor);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false);
Label label = new Label();
mv.visitJumpInsn(IFEQ, label); // means if false
// 3. Generate the code that will call the method on the executor:
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitTypeInsn(CHECKCAST, classname);
String callDescriptor = Utils.insertExtraParameter(classname, ctor.descriptor);
int pcount = Utils.getParameterCount(ctor.descriptor);
if (pcount > maxStack) {
pcount = maxStack;
}
// 4. Unpack parameter array to fit the descriptor for that method
Utils.generateInstructionsToUnpackArrayAccordingToDescriptor(mv, ctor.descriptor, 1);
// ReturnType returnType = Utils.getReturnTypeDescriptor(method.descriptor);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, executorClassName, "___init___", callDescriptor, false);
// if (returnType.isVoid()) {
mv.visitInsn(ACONST_NULL);
// } else if (returnType.isPrimitive()) {
// Utils.insertBoxInsns(mv, returnType.descriptor);
// }
mv.visitInsn(Opcodes.ARETURN);
mv.visitLabel(label);
}
// 5. Throw exception as dynamic dispatcher has been called for something it shouldn't have
// At this point we failed to find it as a method we can dispatch to our executor, so we want
// to pass it 'up' to our supertype. We need to get the dispatcher for our superclass
// and then call the __execute() on it, assuming that it will be able to handle this request.
// alternative 1: use the dispatcher for the superclass
// Determine the supertype
String slashedSupertypeName = rtype.getTypeDescriptor().getSupertypeName();
// getDispatcher will give us the dispatcher for the supertype
mv.visitFieldInsn(Opcodes.GETSTATIC, slashedSupertypeName, fReloadableTypeFieldName, lReloadableType);
mv.visitMethodInsn(INVOKEVIRTUAL, tReloadableType, "getDispatcher",
"()Lorg/springsource/loaded/__DynamicallyDispatchable;", false);
// alternative 2: find the right dispatcher - i.e. who in the super hierarchy provides that nameAndDescriptor
// now invoke the dynamic dispatch call on that dispatcher
mv.visitVarInsn(ALOAD, indexArgs);
mv.visitVarInsn(ALOAD, indexTarget);
mv.visitVarInsn(ALOAD, indexNameAndDescriptor);
mv.visitMethodInsn(INVOKEINTERFACE, tDynamicallyDispatchable, mDynamicDispatchName,
mDynamicDispatchDescriptor, false);
mv.visitInsn(ARETURN);
// mv.visitTypeInsn(NEW, "java/lang/IllegalStateException");
// mv.visitInsn(DUP);
// mv.visitVarInsn(ALOAD, 3);
// mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalStateException", "<init>", "(Ljava/lang/String;)V");
// mv.visitInsn(ATHROW);
mv.visitMaxs(maxStack, 6);
mv.visitEnd();
}
/**
* Called to generate the implementation of a normal method on the interface - a normal method is one that did
* exist when the type was first defined. Might be a catcher.
*/
private void generateRegularMethod(String name, String descriptor, String signature, String[] exceptions) {
// The original descriptor is how it was defined on the original type and how it is defined in the executor class.
// The original descriptor is this descriptor with the first parameter trimmed off.
boolean isClinit = name.equals("___clinit___");
String originalDescriptor = isClinit ? descriptor : Utils.stripFirstParameter(descriptor);
MethodMember method = null;
// Detect if the name has been modified for clash avoidance reasons
if (name.equals("___init___")) {
// it is a ctor
method = rtype.getConstructor(originalDescriptor);
}
else {
if (isClinit) {
generateClinitDispatcher();
return;
}
else {
// TODO need a better solution that these __
if (name.startsWith("__") && !name.equals("__$swapInit")) { // __$swapInit is the groovy reset method
// clash avoidance name
method = rtype.getMethod(name.substring(2), originalDescriptor);
}
else {
method = rtype.getMethod(name, originalDescriptor);
}
}
}
boolean isStatic = method.isStatic();
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, name, descriptor, signature,
exceptions);
mv.visitCode();
// The input descriptor will include the extra initial parameter (the instance, or null for static methods)
ReturnType returnTypeDescriptor = Utils.getReturnTypeDescriptor(descriptor);
// For a static method the first parameter can be ignored
int params = Utils.getParameterCount(descriptor);
String callDescriptor = isStatic ? originalDescriptor : descriptor;
Utils.createLoadsBasedOnDescriptor(mv, callDescriptor, isStatic ? 2 : 1);
mv.visitMethodInsn(INVOKESTATIC, executorClassName, name, callDescriptor, false);
Utils.addCorrectReturnInstruction(mv, returnTypeDescriptor, false);
mv.visitMaxs(params, params + 1);
mv.visitEnd();
}
public void visitOuterClass(String arg0, String arg1, String arg2) {
}
public void visitSource(String arg0, String arg1) {
}
}
}

View File

@@ -1,44 +1,44 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Represents an exact type pattern.
*
* @author Andy Clement
* @since 0.5.0
*/
public class ExactTypePattern extends TypePattern {
private String pattern;
/**
* @param pattern type pattern of the form com.foo.Bar
*/
public ExactTypePattern(String pattern) {
this.pattern = pattern;
}
protected boolean internalMatches(String input) {
boolean b = input.equals(pattern);
return b;
}
public String toString() {
return "text:" + pattern;
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Represents an exact type pattern.
*
* @author Andy Clement
* @since 0.5.0
*/
public class ExactTypePattern extends TypePattern {
private String pattern;
/**
* @param pattern type pattern of the form com.foo.Bar
*/
public ExactTypePattern(String pattern) {
this.pattern = pattern;
}
protected boolean internalMatches(String input) {
boolean b = input.equals(pattern);
return b;
}
public String toString() {
return "text:" + pattern;
}
}

View File

@@ -1,33 +1,33 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.io.File;
/**
* Call back interface for the FileSystemWatcher.
*
* @author Andy Clement
* @since 0.5.0
*/
public interface FileChangeListener {
void fileChanged(File file);
void register(ReloadableType rtype, File file);
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.io.File;
/**
* Call back interface for the FileSystemWatcher.
*
* @author Andy Clement
* @since 0.5.0
*/
public interface FileChangeListener {
void fileChanged(File file);
void register(ReloadableType rtype, File file);
}

View File

@@ -1,188 +1,188 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
/**
* Extract an interface for a type. The interface embodies the shape of the type as originally loaded. The key
* difference with methods in the interface is that they contain an extra (leading) parameter that is the type of the
* original loaded class.<br>
* For example:<br>
*
* <pre>
* <tt>
* class Foo {
* public String foo(int i) {}
* }
* </tt>
* </pre>
*
* will cause creation of an interface method:
*
* <pre>
* <tt>
* String foo(Foo instance, int i) {}
* </tt>
* </pre>
*
* @author Andy Clement
* @since 0.5.0
*/
public class InterfaceExtractor {
@SuppressWarnings("unused")
private TypeRegistry registry;
public InterfaceExtractor(TypeRegistry registry) {
this.registry = registry;
}
/**
* Extract the fixed interface for a class and a type descriptor with more details on the methods.
*
* @param classbytes bytes for the class which is going through interface extraction
* @param registry type registry related to the classloader for this class
* @param typeDescriptor previously extracted type descriptor for the class
* @return class bytes for extracted interface
*/
public static byte[] extract(byte[] classbytes, TypeRegistry registry, TypeDescriptor typeDescriptor) {
return new InterfaceExtractor(registry).extract(classbytes, typeDescriptor);
}
public byte[] extract(byte[] classbytes, TypeDescriptor typeDescriptor) {
ClassReader fileReader = new ClassReader(classbytes);
ExtractorVisitor extractorVisitor = new ExtractorVisitor(typeDescriptor);
fileReader.accept(extractorVisitor, 0);
return extractorVisitor.getBytes();
}
class ExtractorVisitor extends ClassVisitor implements Constants {
private TypeDescriptor typeDescriptor;
private ClassWriter interfaceWriter = new ClassWriter(0);
private String slashedtypename;
public ExtractorVisitor(TypeDescriptor typeDescriptor) {
super(ASM5);
this.typeDescriptor = typeDescriptor;
}
public byte[] getBytes() {
return interfaceWriter.toByteArray();
}
public void visit(int version, int flags, String name, String signature, String superclassName,
String[] interfaceNames) {
// Create interface "public interface [typename]__I {"
interfaceWriter.visit(version, ACC_PUBLIC_INTERFACE, Utils.getInterfaceName(name), null,
"java/lang/Object", null);
this.slashedtypename = name;
}
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
// TODO should we special case statics (and not have them require an extra leading param)?
if (isClinitOrInit(name)) {
if (name.charAt(1) != 'c') { // avoid <clinit>
// It is a constructor
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
// Need a modified name
name = "___init___";
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, signature, exceptions);
}
}
else {
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
// generic signature is erased
MethodMember method = typeDescriptor.getByDescriptor(name, descriptor);
if (MethodMember.isClash(method)) {
name = "__" + name;
}
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, null, exceptions);
}
return null;
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
return null;
}
public void visitInnerClass(String name, String outerName, String innerName, int access) {
// nothing to do
}
public void visitOuterClass(String owner, String name, String desc) {
// nothing to do
}
public void visitSource(String source, String debug) {
// nothing to do
}
public void visitAttribute(Attribute attr) {
// nothing to do
}
public void visitEnd() {
// Must add a method on the interface for the dynamic invocation method
String descriptor = mDynamicDispatchDescriptor;
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mDynamicDispatchName, descriptor, null, null);
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mStaticInitializerName, "()V", null, null);
// Go through catchers on the type descriptor and add the methods to the interface
for (MethodMember method : typeDescriptor.getMethods()) {
if (!MethodMember.isCatcher(method)) {
continue;
}
descriptor = createDescriptorWithPrefixedParameter(method.getDescriptor());
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, method.getName(), descriptor, null,
method.getExceptions());
}
}
/**
* Modify the descriptor to include a leading parameter of the type of the class being visited. For example: if
* visiting type "com.Bar" and hit method "(Ljava/lang/String;)V" then this method will return
* "(Lcom/Bar;Ljava/lang/String;)V"
*
* @return new descriptor with extra leading parameter
*/
private String createDescriptorWithPrefixedParameter(String descriptor) {
StringBuilder newDescriptor = new StringBuilder();
newDescriptor.append("(L").append(slashedtypename).append(";");
newDescriptor.append(descriptor, 1, descriptor.length());
return newDescriptor.toString();
}
private boolean isClinitOrInit(String name) {
return name.charAt(0) == '<';
}
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
/**
* Extract an interface for a type. The interface embodies the shape of the type as originally loaded. The key
* difference with methods in the interface is that they contain an extra (leading) parameter that is the type of the
* original loaded class.<br>
* For example:<br>
*
* <pre>
* <tt>
* class Foo {
* public String foo(int i) {}
* }
* </tt>
* </pre>
*
* will cause creation of an interface method:
*
* <pre>
* <tt>
* String foo(Foo instance, int i) {}
* </tt>
* </pre>
*
* @author Andy Clement
* @since 0.5.0
*/
public class InterfaceExtractor {
@SuppressWarnings("unused")
private TypeRegistry registry;
public InterfaceExtractor(TypeRegistry registry) {
this.registry = registry;
}
/**
* Extract the fixed interface for a class and a type descriptor with more details on the methods.
*
* @param classbytes bytes for the class which is going through interface extraction
* @param registry type registry related to the classloader for this class
* @param typeDescriptor previously extracted type descriptor for the class
* @return class bytes for extracted interface
*/
public static byte[] extract(byte[] classbytes, TypeRegistry registry, TypeDescriptor typeDescriptor) {
return new InterfaceExtractor(registry).extract(classbytes, typeDescriptor);
}
public byte[] extract(byte[] classbytes, TypeDescriptor typeDescriptor) {
ClassReader fileReader = new ClassReader(classbytes);
ExtractorVisitor extractorVisitor = new ExtractorVisitor(typeDescriptor);
fileReader.accept(extractorVisitor, 0);
return extractorVisitor.getBytes();
}
class ExtractorVisitor extends ClassVisitor implements Constants {
private TypeDescriptor typeDescriptor;
private ClassWriter interfaceWriter = new ClassWriter(0);
private String slashedtypename;
public ExtractorVisitor(TypeDescriptor typeDescriptor) {
super(ASM5);
this.typeDescriptor = typeDescriptor;
}
public byte[] getBytes() {
return interfaceWriter.toByteArray();
}
public void visit(int version, int flags, String name, String signature, String superclassName,
String[] interfaceNames) {
// Create interface "public interface [typename]__I {"
interfaceWriter.visit(version, ACC_PUBLIC_INTERFACE, Utils.getInterfaceName(name), null,
"java/lang/Object", null);
this.slashedtypename = name;
}
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature,
String[] exceptions) {
// TODO should we special case statics (and not have them require an extra leading param)?
if (isClinitOrInit(name)) {
if (name.charAt(1) != 'c') { // avoid <clinit>
// It is a constructor
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
// Need a modified name
name = "___init___";
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, signature, exceptions);
}
}
else {
String newDescriptor = createDescriptorWithPrefixedParameter(descriptor);
// generic signature is erased
MethodMember method = typeDescriptor.getByDescriptor(name, descriptor);
if (MethodMember.isClash(method)) {
name = "__" + name;
}
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, name, newDescriptor, null, exceptions);
}
return null;
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
return null;
}
public void visitInnerClass(String name, String outerName, String innerName, int access) {
// nothing to do
}
public void visitOuterClass(String owner, String name, String desc) {
// nothing to do
}
public void visitSource(String source, String debug) {
// nothing to do
}
public void visitAttribute(Attribute attr) {
// nothing to do
}
public void visitEnd() {
// Must add a method on the interface for the dynamic invocation method
String descriptor = mDynamicDispatchDescriptor;
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mDynamicDispatchName, descriptor, null, null);
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, mStaticInitializerName, "()V", null, null);
// Go through catchers on the type descriptor and add the methods to the interface
for (MethodMember method : typeDescriptor.getMethods()) {
if (!MethodMember.isCatcher(method)) {
continue;
}
descriptor = createDescriptorWithPrefixedParameter(method.getDescriptor());
interfaceWriter.visitMethod(ACC_PUBLIC_ABSTRACT, method.getName(), descriptor, null,
method.getExceptions());
}
}
/**
* Modify the descriptor to include a leading parameter of the type of the class being visited. For example: if
* visiting type "com.Bar" and hit method "(Ljava/lang/String;)V" then this method will return
* "(Lcom/Bar;Ljava/lang/String;)V"
*
* @return new descriptor with extra leading parameter
*/
private String createDescriptorWithPrefixedParameter(String descriptor) {
StringBuilder newDescriptor = new StringBuilder();
newDescriptor.append("(L").append(slashedtypename).append(";");
newDescriptor.append(descriptor, 1, descriptor.length());
return newDescriptor.toString();
}
private boolean isClinitOrInit(String name) {
return name.charAt(0) == '<';
}
}
}

View File

@@ -1,45 +1,45 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Represents a double dotted type pattern. For example: com.foo.bar..* - this has the same meaning as in AspectJ.
*
* @author Andy Clement
* @since 0.5.0
*/
public class PrefixTypePattern extends TypePattern {
private String pattern;
/**
* @param pattern the prefix pattern of the form 'com.foo.bar..*'
*/
public PrefixTypePattern(String pattern) {
this.pattern = pattern.substring(0, pattern.length() - 2); // chop off the '.*'
}
protected boolean internalMatches(String input) {
boolean b = input.startsWith(pattern);
return b;
}
public String toString() {
return "text:" + pattern + ".*";
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Represents a double dotted type pattern. For example: com.foo.bar..* - this has the same meaning as in AspectJ.
*
* @author Andy Clement
* @since 0.5.0
*/
public class PrefixTypePattern extends TypePattern {
private String pattern;
/**
* @param pattern the prefix pattern of the form 'com.foo.bar..*'
*/
public PrefixTypePattern(String pattern) {
this.pattern = pattern.substring(0, pattern.length() - 2); // chop off the '.*'
}
protected boolean internalMatches(String input) {
boolean b = input.startsWith(pattern);
return b;
}
public String toString() {
return "text:" + pattern + ".*";
}
}

View File

@@ -1,35 +1,35 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
*
* @author Andy Clement
* @since 0.5.0
*/
@SuppressWarnings("serial")
public class ReloadException extends RuntimeException {
public ReloadException(String message, Exception cause) {
super(message, cause);
}
public ReloadException(String message) {
super(message);
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
*
* @author Andy Clement
* @since 0.5.0
*/
@SuppressWarnings("serial")
public class ReloadException extends RuntimeException {
public ReloadException(String message, Exception cause) {
super(message, cause);
}
public ReloadException(String message) {
super(message);
}
}

View File

@@ -1,357 +1,357 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.util.ArrayList;
import java.util.List;
/**
* Encapsulates the information about a type relevant to reloading. The TypeDescriptor for a type is sometimes extracted
* whilst performing some other operation (eg. {@link InterfaceExtractor}) but can also be retrieved directly using
* {@link TypeDescriptorExtractor}.
*
* @author Andy Clement
* @since 0.5.0
*/
public class TypeDescriptor implements Constants {
private final int modifiers;
final String typename; // slashed
final String supertypeName; // slashed
final String[] superinterfaceNames; // slashed // empty array if there are none
private final MethodMember[] constructors; // empty array if there are none (but this doesn't ever happen!)
private final MethodMember[] methods; // empty array if there are none
private final MethodMember[] nonprivateMethods; // empty array if there are none
private final FieldMember[] fields; // empty array if there are none
private final FieldMember[] fieldsRequiringAccessors; // empty array if there are none
private List<String> finalInHierarchy; // nameAndDescriptor strings for methods final in the hierarchy (e.g. ordinal()I for an enum)
private final TypeRegistry registry;
private final boolean isReloadable;
private final boolean hasClinit;
private final static int IS_GROOVY_TYPE = 0x0001;
private int bits = 0x0000;
private ReloadableType reloadableType;
private int nextId = 0;
public TypeDescriptor(String slashedTypeName, String supertypeName, String[] superinterfaceNames, int modifiers,
List<? extends MethodMember> constructors, List<MethodMember> methods, List<? extends FieldMember> fields,
List<? extends FieldMember> fieldsRequiringAccessors, boolean isReloadable, TypeRegistry registry,
boolean hasClinit,
List<String> finalInHierarchy) {
this.typename = slashedTypeName;
this.supertypeName = supertypeName;
this.superinterfaceNames = (superinterfaceNames == null ? NO_STRINGS : superinterfaceNames);
this.finalInHierarchy = finalInHierarchy;
this.modifiers = modifiers;
this.fields = fields.size() == 0 ? FieldMember.NONE : fields.toArray(new FieldMember[fields.size()]);
this.fieldsRequiringAccessors = fieldsRequiringAccessors.size() == 0 ? FieldMember.NONE
: fieldsRequiringAccessors
.toArray(new FieldMember[fieldsRequiringAccessors.size()]);
this.constructors = constructors.size() == 0 ? MethodMember.NONE
: constructors.toArray(new MethodMember[constructors
.size()]);
this.methods = methods.size() == 0 ? MethodMember.NONE : methods.toArray(new MethodMember[methods.size()]);
this.nonprivateMethods = filterNonPrivateMethods(this.methods);
this.isReloadable = isReloadable;
this.registry = registry;
this.hasClinit = hasClinit;
allocateIds();
}
private static MethodMember[] filterNonPrivateMethods(MethodMember[] allMethods) {
List<MethodMember> result = null;
for (MethodMember method : allMethods) {
if (!method.isPrivate()) {
if (result == null) {
result = new ArrayList<MethodMember>();
}
result.add(method);
}
}
if (result == null) {
return MethodMember.NONE;
}
else {
return result.toArray(new MethodMember[result.size()]);
}
}
private void allocateIds() {
// Give the methods awareness of their index
for (MethodMember method : methods) {
method.setId(nextId++);
}
}
public MethodMember[] getMethods() {
return methods;
}
public MethodMember[] getConstructors() {
return constructors;
}
public FieldMember[] getFields() {
return fields;
}
public FieldMember[] getFieldsRequiringAccessors() {
return fieldsRequiringAccessors;
}
public int getModifiers() {
return modifiers;
}
/**
* @return the (slashed) type name
*/
public String getName() {
return typename;
}
/**
* @return the (slashed) supertype name
*/
public String getSupertypeName() {
return supertypeName;
}
/**
* @return array of (slashed) superinterface names (or an empty array if none)
*/
public String[] getSuperinterfacesName() {
return superinterfaceNames;
}
/**
* Check if this descriptor defines the specified method. A strict check on all aspects of the method -
* names/exceptions/flags, etc.
*
* @param method the method to check the existence of in this type descriptor
* @return true if this descriptor defines the specified method
*/
public boolean defines(MethodMember method) {
for (MethodMember existingMethod : methods) {
// make sure it *really* defines it (i.e. it is not a catcher)
if (!MethodMember.isCatcher(existingMethod) && existingMethod.equals(method)) {
return true;
}
}
return false;
}
/**
* Check if this descriptor defines a method with the specified name and descriptor. Return the method if it is
* found. Modifiers, generic signature and exceptions are ignored in this search.
*
* @param name the member name
* @param descriptor the member descriptor (e.g. (Ljava/lang/String;)I)
* @return the MethodMember if there is one
*/
public MethodMember getByDescriptor(String name, String descriptor) {
for (MethodMember existingMethod : methods) {
if (existingMethod.getName().equals(name) && existingMethod.getDescriptor().equals(descriptor)) {
return existingMethod;
}
}
return null;
}
public MethodMember getByNameAndDescriptor(String nameAndDescriptor) {
for (MethodMember existingMethod : methods) {
if (nameAndDescriptor.startsWith(existingMethod.getName())
&& nameAndDescriptor.endsWith(existingMethod.getDescriptor())) {
return existingMethod;
}
}
return null;
}
/**
* @return true if this type descriptor has been created for a reloadable type
*/
public boolean isReloadable() {
return isReloadable;
}
public MethodMember getMethod(int methodId) {
// Should never be an AIOOBE if the woven code is behaving
return methods[methodId];
}
public MethodMember getConstructor(int ctorId) {
// Should never be an AIOOBE if the woven code is behaving
return constructors[ctorId];
}
/**
* @return true if the type is an interface
*/
public boolean isInterface() {
return (modifiers & ACC_INTERFACE) != 0;
}
/**
* @return true if the type is an annotation
*/
public boolean isAnnotation() {
return (modifiers & ACC_ANNOTATION) != 0;
}
/**
* @return true if the type is an enum
*/
public boolean isEnum() {
return (modifiers & ACC_ENUM) != 0;
}
public boolean definesNonPrivate(String nameAndDescriptor) {
for (MethodMember existingMethod : nonprivateMethods) {
if (existingMethod.nameAndDescriptor.equals(nameAndDescriptor)) {
return true;
}
}
return false;
}
public boolean isFinalInHierarchy(String nad) {
return finalInHierarchy.contains(nad);
}
/**
* Search for a field on this type descriptor - do not try supertypes. This lookup does not differentiate between
* static/instance fields.
*
* @param name the name of the field
* @return a FieldMember if the field is found, otherwise null
*/
public FieldMember getField(String name) {
for (FieldMember field : fields) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
public ReloadableType getReloadableType() {
if (!isReloadable) {
return null;
}
if (reloadableType == null) {
reloadableType = registry.getReloadableType(this.typename);
if (reloadableType == null) {
throw new IllegalStateException("There is no ReloadableType instance for " + typename);
}
}
return reloadableType;
}
public TypeRegistry getTypeRegistry() {
return registry;
}
// could be worth caching if used for more than error messages...
public String getDottedName() {
return getName().replace('/', '.');
}
public MethodMember getConstructor(String desc) {
for (MethodMember ctor : constructors) {
String d = ctor.getDescriptor();
if (d.equals(desc)) {
return ctor;
}
}
return null;
}
public boolean isGroovyType() {
return (bits & IS_GROOVY_TYPE) != 0;
}
public void setIsGroovyType(boolean b) {
bits |= IS_GROOVY_TYPE;
}
public boolean hasClinit() {
return hasClinit;
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append("TypeDescriptor: name=" + typename + " superclass=" + supertypeName + " superinterfaces="
+ interfacesToString());
s.append(" flags=0x" + Integer.toHexString(modifiers).toUpperCase()).append("\n");
s.append("Fields: #" + fields.length + "\n" + fieldsToString());
s.append("Constructors:#" + constructors.length + "\n" + methodsToString(constructors));
s.append("Methods:#" + methods.length + "\n" + methodsToString(methods));
return s.toString();
}
private String fieldsToString() {
StringBuilder s = new StringBuilder();
int count = 0;
for (FieldMember field : fields) {
s.append(" field #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(field.toString()).append('\n');
}
return s.toString();
}
private String interfacesToString() {
if (superinterfaceNames == null) {
return "";
}
else {
StringBuilder s = new StringBuilder();
for (String superinterfaceName : superinterfaceNames) {
s.append(superinterfaceName);
s.append(" ");
}
return s.toString().trim();
}
}
public String methodsToString(MethodMember[] methods) {
StringBuilder s = new StringBuilder();
int count = 0;
for (MethodMember method : methods) {
s.append(" method #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(method.toString()).append(
" ")
.append(method.bitsToString()).append('\n');
}
return s.toString();
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.util.ArrayList;
import java.util.List;
/**
* Encapsulates the information about a type relevant to reloading. The TypeDescriptor for a type is sometimes extracted
* whilst performing some other operation (eg. {@link InterfaceExtractor}) but can also be retrieved directly using
* {@link TypeDescriptorExtractor}.
*
* @author Andy Clement
* @since 0.5.0
*/
public class TypeDescriptor implements Constants {
private final int modifiers;
final String typename; // slashed
final String supertypeName; // slashed
final String[] superinterfaceNames; // slashed // empty array if there are none
private final MethodMember[] constructors; // empty array if there are none (but this doesn't ever happen!)
private final MethodMember[] methods; // empty array if there are none
private final MethodMember[] nonprivateMethods; // empty array if there are none
private final FieldMember[] fields; // empty array if there are none
private final FieldMember[] fieldsRequiringAccessors; // empty array if there are none
private List<String> finalInHierarchy; // nameAndDescriptor strings for methods final in the hierarchy (e.g. ordinal()I for an enum)
private final TypeRegistry registry;
private final boolean isReloadable;
private final boolean hasClinit;
private final static int IS_GROOVY_TYPE = 0x0001;
private int bits = 0x0000;
private ReloadableType reloadableType;
private int nextId = 0;
public TypeDescriptor(String slashedTypeName, String supertypeName, String[] superinterfaceNames, int modifiers,
List<? extends MethodMember> constructors, List<MethodMember> methods, List<? extends FieldMember> fields,
List<? extends FieldMember> fieldsRequiringAccessors, boolean isReloadable, TypeRegistry registry,
boolean hasClinit,
List<String> finalInHierarchy) {
this.typename = slashedTypeName;
this.supertypeName = supertypeName;
this.superinterfaceNames = (superinterfaceNames == null ? NO_STRINGS : superinterfaceNames);
this.finalInHierarchy = finalInHierarchy;
this.modifiers = modifiers;
this.fields = fields.size() == 0 ? FieldMember.NONE : fields.toArray(new FieldMember[fields.size()]);
this.fieldsRequiringAccessors = fieldsRequiringAccessors.size() == 0 ? FieldMember.NONE
: fieldsRequiringAccessors
.toArray(new FieldMember[fieldsRequiringAccessors.size()]);
this.constructors = constructors.size() == 0 ? MethodMember.NONE
: constructors.toArray(new MethodMember[constructors
.size()]);
this.methods = methods.size() == 0 ? MethodMember.NONE : methods.toArray(new MethodMember[methods.size()]);
this.nonprivateMethods = filterNonPrivateMethods(this.methods);
this.isReloadable = isReloadable;
this.registry = registry;
this.hasClinit = hasClinit;
allocateIds();
}
private static MethodMember[] filterNonPrivateMethods(MethodMember[] allMethods) {
List<MethodMember> result = null;
for (MethodMember method : allMethods) {
if (!method.isPrivate()) {
if (result == null) {
result = new ArrayList<MethodMember>();
}
result.add(method);
}
}
if (result == null) {
return MethodMember.NONE;
}
else {
return result.toArray(new MethodMember[result.size()]);
}
}
private void allocateIds() {
// Give the methods awareness of their index
for (MethodMember method : methods) {
method.setId(nextId++);
}
}
public MethodMember[] getMethods() {
return methods;
}
public MethodMember[] getConstructors() {
return constructors;
}
public FieldMember[] getFields() {
return fields;
}
public FieldMember[] getFieldsRequiringAccessors() {
return fieldsRequiringAccessors;
}
public int getModifiers() {
return modifiers;
}
/**
* @return the (slashed) type name
*/
public String getName() {
return typename;
}
/**
* @return the (slashed) supertype name
*/
public String getSupertypeName() {
return supertypeName;
}
/**
* @return array of (slashed) superinterface names (or an empty array if none)
*/
public String[] getSuperinterfacesName() {
return superinterfaceNames;
}
/**
* Check if this descriptor defines the specified method. A strict check on all aspects of the method -
* names/exceptions/flags, etc.
*
* @param method the method to check the existence of in this type descriptor
* @return true if this descriptor defines the specified method
*/
public boolean defines(MethodMember method) {
for (MethodMember existingMethod : methods) {
// make sure it *really* defines it (i.e. it is not a catcher)
if (!MethodMember.isCatcher(existingMethod) && existingMethod.equals(method)) {
return true;
}
}
return false;
}
/**
* Check if this descriptor defines a method with the specified name and descriptor. Return the method if it is
* found. Modifiers, generic signature and exceptions are ignored in this search.
*
* @param name the member name
* @param descriptor the member descriptor (e.g. (Ljava/lang/String;)I)
* @return the MethodMember if there is one
*/
public MethodMember getByDescriptor(String name, String descriptor) {
for (MethodMember existingMethod : methods) {
if (existingMethod.getName().equals(name) && existingMethod.getDescriptor().equals(descriptor)) {
return existingMethod;
}
}
return null;
}
public MethodMember getByNameAndDescriptor(String nameAndDescriptor) {
for (MethodMember existingMethod : methods) {
if (nameAndDescriptor.startsWith(existingMethod.getName())
&& nameAndDescriptor.endsWith(existingMethod.getDescriptor())) {
return existingMethod;
}
}
return null;
}
/**
* @return true if this type descriptor has been created for a reloadable type
*/
public boolean isReloadable() {
return isReloadable;
}
public MethodMember getMethod(int methodId) {
// Should never be an AIOOBE if the woven code is behaving
return methods[methodId];
}
public MethodMember getConstructor(int ctorId) {
// Should never be an AIOOBE if the woven code is behaving
return constructors[ctorId];
}
/**
* @return true if the type is an interface
*/
public boolean isInterface() {
return (modifiers & ACC_INTERFACE) != 0;
}
/**
* @return true if the type is an annotation
*/
public boolean isAnnotation() {
return (modifiers & ACC_ANNOTATION) != 0;
}
/**
* @return true if the type is an enum
*/
public boolean isEnum() {
return (modifiers & ACC_ENUM) != 0;
}
public boolean definesNonPrivate(String nameAndDescriptor) {
for (MethodMember existingMethod : nonprivateMethods) {
if (existingMethod.nameAndDescriptor.equals(nameAndDescriptor)) {
return true;
}
}
return false;
}
public boolean isFinalInHierarchy(String nad) {
return finalInHierarchy.contains(nad);
}
/**
* Search for a field on this type descriptor - do not try supertypes. This lookup does not differentiate between
* static/instance fields.
*
* @param name the name of the field
* @return a FieldMember if the field is found, otherwise null
*/
public FieldMember getField(String name) {
for (FieldMember field : fields) {
if (field.getName().equals(name)) {
return field;
}
}
return null;
}
public ReloadableType getReloadableType() {
if (!isReloadable) {
return null;
}
if (reloadableType == null) {
reloadableType = registry.getReloadableType(this.typename);
if (reloadableType == null) {
throw new IllegalStateException("There is no ReloadableType instance for " + typename);
}
}
return reloadableType;
}
public TypeRegistry getTypeRegistry() {
return registry;
}
// could be worth caching if used for more than error messages...
public String getDottedName() {
return getName().replace('/', '.');
}
public MethodMember getConstructor(String desc) {
for (MethodMember ctor : constructors) {
String d = ctor.getDescriptor();
if (d.equals(desc)) {
return ctor;
}
}
return null;
}
public boolean isGroovyType() {
return (bits & IS_GROOVY_TYPE) != 0;
}
public void setIsGroovyType(boolean b) {
bits |= IS_GROOVY_TYPE;
}
public boolean hasClinit() {
return hasClinit;
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append("TypeDescriptor: name=" + typename + " superclass=" + supertypeName + " superinterfaces="
+ interfacesToString());
s.append(" flags=0x" + Integer.toHexString(modifiers).toUpperCase()).append("\n");
s.append("Fields: #" + fields.length + "\n" + fieldsToString());
s.append("Constructors:#" + constructors.length + "\n" + methodsToString(constructors));
s.append("Methods:#" + methods.length + "\n" + methodsToString(methods));
return s.toString();
}
private String fieldsToString() {
StringBuilder s = new StringBuilder();
int count = 0;
for (FieldMember field : fields) {
s.append(" field #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(field.toString()).append('\n');
}
return s.toString();
}
private String interfacesToString() {
if (superinterfaceNames == null) {
return "";
}
else {
StringBuilder s = new StringBuilder();
for (String superinterfaceName : superinterfaceNames) {
s.append(superinterfaceName);
s.append(" ");
}
return s.toString().trim();
}
}
public String methodsToString(MethodMember[] methods) {
StringBuilder s = new StringBuilder();
int count = 0;
for (MethodMember method : methods) {
s.append(" method #" + Utils.toPaddedNumber((count++), 3)).append(' ').append(method.toString()).append(
" ")
.append(method.bitsToString()).append('\n');
}
return s.toString();
}
}

View File

@@ -1,389 +1,389 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A type descriptor describes the type, methods, fields, etc - two type descriptors are comparable to discover what has
* changed between versions.
*
* @author Andy Clement
* @since 0.5.0
*/
public class TypeDescriptorExtractor {
private final static boolean DEBUG_TYPE_DESCRIPTOR_EXTRACTOR = false;
private TypeRegistry registry;
public TypeDescriptorExtractor(TypeRegistry registry) {
this.registry = registry;
}
public TypeDescriptor extract(byte[] bytes, boolean isReloadableType) {
ClassReader fileReader = new ClassReader(bytes);
ExtractionVisitor extractionVisitor = new ExtractionVisitor(isReloadableType);
fileReader.accept(extractionVisitor, 0);
return extractionVisitor.getTypeDescriptor();
}
/**
* Visit a class and accumulate sufficient information to build a TypeDescriptor.
*/
class ExtractionVisitor extends ClassVisitor implements Opcodes {
private boolean isReloadableType;
private int flags;
private String typename;
private String superclassName;
private String[] interfaceNames;
private boolean isGroovy = false;
private boolean isEnum = false;
private boolean hasClinit = false;
// TODO [perf - reduce garbage] make these collections lazily initialize
private List<MethodMember> constructors = new ArrayList<MethodMember>();
private List<MethodMember> methods = new ArrayList<MethodMember>();
private List<FieldMember> fieldsRequiringAccessors = new ArrayList<FieldMember>();
private List<FieldMember> fields = new ArrayList<FieldMember>();
private List<String> finalInHierarchy = new ArrayList<String>();
public ExtractionVisitor(boolean isReloadableType) {
super(ASM5);
this.isReloadableType = isReloadableType;
}
public TypeDescriptor getTypeDescriptor() {
if (isReloadableType) {
computeCatchersAndSuperdispatchers();
}
computeFieldsRequiringAccessors();
computeClashes();
TypeDescriptor td = new TypeDescriptor(typename, superclassName, interfaceNames, flags, constructors,
methods, fields,
fieldsRequiringAccessors, isReloadableType, registry, hasClinit, finalInHierarchy);
if (isGroovy) {
td.setIsGroovyType(true);
}
return td;
}
/**
* Determine if there are clashes. A clash is where a static method takes the reloadable type as its first
* parameter but in all other ways is the same as an existing instance method. For example this instance method
* A.foo(String) clashes with this static method A.foo(A, String). 'clashing' means the executor will have to do
* something to avoid a duplicate method problem and we'll have to differentiate between the two.
*/
private void computeClashes() {
String clashDescriptorPrefix = "(L" + typename + ";";
for (MethodMember member : methods) {
if (member.isStatic()) {
String desc = member.descriptor;
if (desc.startsWith(clashDescriptorPrefix)) {
// might be a clash, need to check the instance methods
for (MethodMember member2 : methods) {
if (member2.name.equals(member.name)) {
// really might be a clash
String instanceParams = member2.descriptor;
instanceParams = instanceParams.substring(1, instanceParams.indexOf(')') + 1);
String staticParams = desc.substring(clashDescriptorPrefix.length(),
desc.indexOf(')') + 1);
if (instanceParams.equals(staticParams)) {
// CLASH
member.bits |= MethodMember.BIT_CLASH;
}
}
}
}
}
}
}
private TypeDescriptor getTypeDescriptorFor(String slashedname) {
return registry.getDescriptorFor(slashedname);
}
// TODO [refactor] extract the type registry relationship code into a central helper class
private TypeDescriptor findTypeDescriptor(TypeRegistry registry, String typename) {
// follow the pattern for a classloader: recurse up trying to find it, then recurse down trying to load it
TypeRegistry regToTry = registry;
TypeDescriptor td = regToTry.getDescriptorForReloadableType(typename);
while (td == null) {
regToTry = regToTry.getParentRegistry();
if (regToTry == null) {
break;
}
td = regToTry.getDescriptorForReloadableType(typename);
}
if (td == null) {
td = getTypeDescriptorFor(typename);
}
return td;
}
/**
* Algorithm: Go up the superclass hierarchy for a type and determine what should be caught in this type (see
* 'catchers' in notes.md). Methods that are private, static or final do *not* get a catcher. This method also
* computes superdispatchers - see 'superdispatchers' in notes.md
*
*/
private void walkHierarchyForCatchersAndSuperDispatchers(String superclass, List<String> superDispatchers,
List<String> finalInHierarchy) {
TypeDescriptor supertypeDescriptor = superclass == null ? null : findTypeDescriptor(registry, superclass);
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
System.out.println("Computing catchers on " + this.typename + " from superclass " + superclass);
}
boolean isReloadable = supertypeDescriptor.isReloadable();
for (MethodMember method : supertypeDescriptor.getMethods()) {
if (shouldCreateSuperDispatcherFor(method) && !superDispatchers.contains(method.nameAndDescriptor)) {
// need a public super dispatcher - so that we can reach that super method
// from a reloaded instance of this type
MethodMember superdispatcher = method.superDispatcherFor();
methods.add(superdispatcher);
superDispatchers.add(method.nameAndDescriptor);
}
if (shouldCatchMethod(method) && !finalInHierarchy.contains(method.getNameAndDescriptor())) {
// don't need the catcher if method is already defined since when the existing method is rewritten
// it will be kind of morphed into a catcher
// TODO what about a private method that is overridden by a static method (same name/descriptor but not
// an overrides relationship)
if (!isReloadable && Modifier.isFinal(method.getModifiers())) {
// Do not create a catcher, the supertype is not reloadable and so an implementation cannot be
// added lower in the type hierarchy
finalInHierarchy.add(method.getNameAndDescriptor());
continue;
}
MethodMember found = null;
for (MethodMember existingMethod : methods) {
if (existingMethod.equalsApartFromModifiers(method)) {
found = existingMethod;
break;
}
}
if (found != null) {
continue;
}
MethodMember catcherCopy = method.catcherCopyOf();
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
System.out.println("Adding catcher for " + method.nameAndDescriptor);
}
methods.add(catcherCopy);
}
else {
if (method.isFinal()) {
finalInHierarchy.add(method.getNameAndDescriptor());
}
}
}
if (supertypeDescriptor.supertypeName != null) {
walkHierarchyForCatchersAndSuperDispatchers(supertypeDescriptor.supertypeName, superDispatchers,
finalInHierarchy);
}
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
// abstract class may be missing methods that it can implement from the interfaces
for (String interfaceName : supertypeDescriptor.superinterfaceNames) {
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInHierarchy);
}
}
}
/**
* Compute and add the catch methods and super dispatch methods that apply to this type.
*/
private void computeCatchersAndSuperdispatchers() {
if (Modifier.isInterface(this.flags)) { // Don't need catchers in interfaces
return;
}
// TODO [review design] review the need to create catchers for methods where the supertype is reloadable.
// Can we just add them to the topmost reloadable type?
List<String> doNotCatch = new ArrayList<String>();
walkHierarchyForCatchersAndSuperDispatchers(superclassName, new ArrayList<String>(), doNotCatch);
// ought to look in interfaces if we are an abstract class
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
// abstract class may be missing methods that it can implement from the interfaces
for (String interfaceName : interfaceNames) {
addCatchersForNonImplementedMethodsFrom(interfaceName, doNotCatch);
}
}
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
System.out.println("For " + this.typename + " setting finalsInHierarchy to " + doNotCatch);
}
finalInHierarchy.addAll(doNotCatch);
}
// TODO should clone and finalize be in here?
private boolean shouldCreateSuperDispatcherFor(MethodMember method) {
return method.isProtected() && !(
(method.getName().equals("finalize") && method.getDescriptor().equals("()V")) ||
(method.getName().equals("clone") && method.getDescriptor().equals("()Ljava/lang/Object;")));
}
private void addCatchersForNonImplementedMethodsFrom(String interfacename, List<String> finalInNonReloadableType) {
TypeDescriptor interfaceDescriptor = findTypeDescriptor(registry, interfacename);
for (MethodMember method : interfaceDescriptor.getMethods()) {
// If this class doesn't implement this interface method, add it
boolean found = false;
for (MethodMember existingMethod : methods) {
if (existingMethod.equalsApartFromModifiers(method)) {
found = true;
break;
}
}
if (!found && !finalInNonReloadableType.contains(method.getNameAndDescriptor())) {
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
Log.log("adding catcher for [" + method + "] from [" + interfacename + "] to [" + this.typename
+ "]");
}
methods.add(method.catcherCopyOfWithAbstractRemoved());
}
}
for (String interfaceName : interfaceDescriptor.superinterfaceNames) {
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInNonReloadableType);
}
}
/**
* Protected fields in reloadable parents of a class need an accessor adding to the reloadable type so that the
* fields can be reached from the executor.
*/
private void computeFieldsRequiringAccessors() {
String type = superclassName;
while (type != null) {
TypeDescriptor supertypeDescriptor = findTypeDescriptor(registry, type);
if (!supertypeDescriptor.isReloadable()) {
for (FieldMember field : supertypeDescriptor.getFields()) {
if (field.isProtected()) {
boolean found = false;
for (FieldMember existingField : fields) {
if (existingField.getName().equals(field.getName())) {
// no need for accessor... this type defines a field that overrides it
found = true;
break;
}
}
if (!found) {
fieldsRequiringAccessors.add(field);
}
}
}
}
type = supertypeDescriptor.supertypeName;
}
}
/**
* Determine if a method gets a catcher. Deliberately not catching final methods, static methods, private
* methods or finalize()V.
*
* @return true if it should be caught
*/
private boolean shouldCatchMethod(MethodMember method) {
return !(method.isPrivateOrStaticOrFinal()
|| method.getName().endsWith(Constants.methodSuffixSuperDispatcher) || (method.getName().equals(
"finalize") && method.getDescriptor().equals("()V")));
}
public void visit(int version, int flags, String name, String signature, String superclassName,
String[] interfaceNames) {
this.flags = flags;
this.superclassName = superclassName;
this.interfaceNames = interfaceNames;
if (superclassName != null && superclassName.equals("java/lang/Enum")) {
this.isEnum = true;
}
this.typename = name;
}
public AnnotationVisitor visitAnnotation(String classDesc, boolean isRuntime) {
return null;
}
public void visitAttribute(Attribute attribute) {
}
public void visitInnerClass(String name, String outername, String innerName, int access) {
if (name.equals(typename)) {
this.flags = access;
}
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
fields.add(new FieldMember(typename, access, name, desc, signature));
if (name.equals("$callSiteArray")) {
isGroovy = true;
}
return null;
}
/**
* Visit a method in the class and build an appropriate representation for it to include in the extracted
* output.
*/
public MethodVisitor visitMethod(int flags, String name, String descriptor, String genericSignature,
String[] exceptions) {
if (name.charAt(0) != '<') {
methods.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
}
else {
if (name.equals("<init>")) {
//Even though constructors are not reloadable at present, we need to add them to type descriptors to know
//about their original modifiers (these are promoted to public to allow executors access to them).
constructors.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
}
else if (name.equals("<clinit>")) {
hasClinit = true;
}
}
return null;
}
public void visitOuterClass(String owner, String name, String desc) {
}
public void visitSource(String source, String debug) {
}
public void visitEnd() {
}
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* A type descriptor describes the type, methods, fields, etc - two type descriptors are comparable to discover what has
* changed between versions.
*
* @author Andy Clement
* @since 0.5.0
*/
public class TypeDescriptorExtractor {
private final static boolean DEBUG_TYPE_DESCRIPTOR_EXTRACTOR = false;
private TypeRegistry registry;
public TypeDescriptorExtractor(TypeRegistry registry) {
this.registry = registry;
}
public TypeDescriptor extract(byte[] bytes, boolean isReloadableType) {
ClassReader fileReader = new ClassReader(bytes);
ExtractionVisitor extractionVisitor = new ExtractionVisitor(isReloadableType);
fileReader.accept(extractionVisitor, 0);
return extractionVisitor.getTypeDescriptor();
}
/**
* Visit a class and accumulate sufficient information to build a TypeDescriptor.
*/
class ExtractionVisitor extends ClassVisitor implements Opcodes {
private boolean isReloadableType;
private int flags;
private String typename;
private String superclassName;
private String[] interfaceNames;
private boolean isGroovy = false;
private boolean isEnum = false;
private boolean hasClinit = false;
// TODO [perf - reduce garbage] make these collections lazily initialize
private List<MethodMember> constructors = new ArrayList<MethodMember>();
private List<MethodMember> methods = new ArrayList<MethodMember>();
private List<FieldMember> fieldsRequiringAccessors = new ArrayList<FieldMember>();
private List<FieldMember> fields = new ArrayList<FieldMember>();
private List<String> finalInHierarchy = new ArrayList<String>();
public ExtractionVisitor(boolean isReloadableType) {
super(ASM5);
this.isReloadableType = isReloadableType;
}
public TypeDescriptor getTypeDescriptor() {
if (isReloadableType) {
computeCatchersAndSuperdispatchers();
}
computeFieldsRequiringAccessors();
computeClashes();
TypeDescriptor td = new TypeDescriptor(typename, superclassName, interfaceNames, flags, constructors,
methods, fields,
fieldsRequiringAccessors, isReloadableType, registry, hasClinit, finalInHierarchy);
if (isGroovy) {
td.setIsGroovyType(true);
}
return td;
}
/**
* Determine if there are clashes. A clash is where a static method takes the reloadable type as its first
* parameter but in all other ways is the same as an existing instance method. For example this instance method
* A.foo(String) clashes with this static method A.foo(A, String). 'clashing' means the executor will have to do
* something to avoid a duplicate method problem and we'll have to differentiate between the two.
*/
private void computeClashes() {
String clashDescriptorPrefix = "(L" + typename + ";";
for (MethodMember member : methods) {
if (member.isStatic()) {
String desc = member.descriptor;
if (desc.startsWith(clashDescriptorPrefix)) {
// might be a clash, need to check the instance methods
for (MethodMember member2 : methods) {
if (member2.name.equals(member.name)) {
// really might be a clash
String instanceParams = member2.descriptor;
instanceParams = instanceParams.substring(1, instanceParams.indexOf(')') + 1);
String staticParams = desc.substring(clashDescriptorPrefix.length(),
desc.indexOf(')') + 1);
if (instanceParams.equals(staticParams)) {
// CLASH
member.bits |= MethodMember.BIT_CLASH;
}
}
}
}
}
}
}
private TypeDescriptor getTypeDescriptorFor(String slashedname) {
return registry.getDescriptorFor(slashedname);
}
// TODO [refactor] extract the type registry relationship code into a central helper class
private TypeDescriptor findTypeDescriptor(TypeRegistry registry, String typename) {
// follow the pattern for a classloader: recurse up trying to find it, then recurse down trying to load it
TypeRegistry regToTry = registry;
TypeDescriptor td = regToTry.getDescriptorForReloadableType(typename);
while (td == null) {
regToTry = regToTry.getParentRegistry();
if (regToTry == null) {
break;
}
td = regToTry.getDescriptorForReloadableType(typename);
}
if (td == null) {
td = getTypeDescriptorFor(typename);
}
return td;
}
/**
* Algorithm: Go up the superclass hierarchy for a type and determine what should be caught in this type (see
* 'catchers' in notes.md). Methods that are private, static or final do *not* get a catcher. This method also
* computes superdispatchers - see 'superdispatchers' in notes.md
*
*/
private void walkHierarchyForCatchersAndSuperDispatchers(String superclass, List<String> superDispatchers,
List<String> finalInHierarchy) {
TypeDescriptor supertypeDescriptor = superclass == null ? null : findTypeDescriptor(registry, superclass);
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
System.out.println("Computing catchers on " + this.typename + " from superclass " + superclass);
}
boolean isReloadable = supertypeDescriptor.isReloadable();
for (MethodMember method : supertypeDescriptor.getMethods()) {
if (shouldCreateSuperDispatcherFor(method) && !superDispatchers.contains(method.nameAndDescriptor)) {
// need a public super dispatcher - so that we can reach that super method
// from a reloaded instance of this type
MethodMember superdispatcher = method.superDispatcherFor();
methods.add(superdispatcher);
superDispatchers.add(method.nameAndDescriptor);
}
if (shouldCatchMethod(method) && !finalInHierarchy.contains(method.getNameAndDescriptor())) {
// don't need the catcher if method is already defined since when the existing method is rewritten
// it will be kind of morphed into a catcher
// TODO what about a private method that is overridden by a static method (same name/descriptor but not
// an overrides relationship)
if (!isReloadable && Modifier.isFinal(method.getModifiers())) {
// Do not create a catcher, the supertype is not reloadable and so an implementation cannot be
// added lower in the type hierarchy
finalInHierarchy.add(method.getNameAndDescriptor());
continue;
}
MethodMember found = null;
for (MethodMember existingMethod : methods) {
if (existingMethod.equalsApartFromModifiers(method)) {
found = existingMethod;
break;
}
}
if (found != null) {
continue;
}
MethodMember catcherCopy = method.catcherCopyOf();
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
System.out.println("Adding catcher for " + method.nameAndDescriptor);
}
methods.add(catcherCopy);
}
else {
if (method.isFinal()) {
finalInHierarchy.add(method.getNameAndDescriptor());
}
}
}
if (supertypeDescriptor.supertypeName != null) {
walkHierarchyForCatchersAndSuperDispatchers(supertypeDescriptor.supertypeName, superDispatchers,
finalInHierarchy);
}
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
// abstract class may be missing methods that it can implement from the interfaces
for (String interfaceName : supertypeDescriptor.superinterfaceNames) {
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInHierarchy);
}
}
}
/**
* Compute and add the catch methods and super dispatch methods that apply to this type.
*/
private void computeCatchersAndSuperdispatchers() {
if (Modifier.isInterface(this.flags)) { // Don't need catchers in interfaces
return;
}
// TODO [review design] review the need to create catchers for methods where the supertype is reloadable.
// Can we just add them to the topmost reloadable type?
List<String> doNotCatch = new ArrayList<String>();
walkHierarchyForCatchersAndSuperDispatchers(superclassName, new ArrayList<String>(), doNotCatch);
// ought to look in interfaces if we are an abstract class
if (Modifier.isAbstract(this.flags) && !this.isEnum/* && !Modifier.isInterface(this.flags)*/) {
// abstract class may be missing methods that it can implement from the interfaces
for (String interfaceName : interfaceNames) {
addCatchersForNonImplementedMethodsFrom(interfaceName, doNotCatch);
}
}
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
System.out.println("For " + this.typename + " setting finalsInHierarchy to " + doNotCatch);
}
finalInHierarchy.addAll(doNotCatch);
}
// TODO should clone and finalize be in here?
private boolean shouldCreateSuperDispatcherFor(MethodMember method) {
return method.isProtected() && !(
(method.getName().equals("finalize") && method.getDescriptor().equals("()V")) ||
(method.getName().equals("clone") && method.getDescriptor().equals("()Ljava/lang/Object;")));
}
private void addCatchersForNonImplementedMethodsFrom(String interfacename, List<String> finalInNonReloadableType) {
TypeDescriptor interfaceDescriptor = findTypeDescriptor(registry, interfacename);
for (MethodMember method : interfaceDescriptor.getMethods()) {
// If this class doesn't implement this interface method, add it
boolean found = false;
for (MethodMember existingMethod : methods) {
if (existingMethod.equalsApartFromModifiers(method)) {
found = true;
break;
}
}
if (!found && !finalInNonReloadableType.contains(method.getNameAndDescriptor())) {
if (DEBUG_TYPE_DESCRIPTOR_EXTRACTOR) {
Log.log("adding catcher for [" + method + "] from [" + interfacename + "] to [" + this.typename
+ "]");
}
methods.add(method.catcherCopyOfWithAbstractRemoved());
}
}
for (String interfaceName : interfaceDescriptor.superinterfaceNames) {
addCatchersForNonImplementedMethodsFrom(interfaceName, finalInNonReloadableType);
}
}
/**
* Protected fields in reloadable parents of a class need an accessor adding to the reloadable type so that the
* fields can be reached from the executor.
*/
private void computeFieldsRequiringAccessors() {
String type = superclassName;
while (type != null) {
TypeDescriptor supertypeDescriptor = findTypeDescriptor(registry, type);
if (!supertypeDescriptor.isReloadable()) {
for (FieldMember field : supertypeDescriptor.getFields()) {
if (field.isProtected()) {
boolean found = false;
for (FieldMember existingField : fields) {
if (existingField.getName().equals(field.getName())) {
// no need for accessor... this type defines a field that overrides it
found = true;
break;
}
}
if (!found) {
fieldsRequiringAccessors.add(field);
}
}
}
}
type = supertypeDescriptor.supertypeName;
}
}
/**
* Determine if a method gets a catcher. Deliberately not catching final methods, static methods, private
* methods or finalize()V.
*
* @return true if it should be caught
*/
private boolean shouldCatchMethod(MethodMember method) {
return !(method.isPrivateOrStaticOrFinal()
|| method.getName().endsWith(Constants.methodSuffixSuperDispatcher) || (method.getName().equals(
"finalize") && method.getDescriptor().equals("()V")));
}
public void visit(int version, int flags, String name, String signature, String superclassName,
String[] interfaceNames) {
this.flags = flags;
this.superclassName = superclassName;
this.interfaceNames = interfaceNames;
if (superclassName != null && superclassName.equals("java/lang/Enum")) {
this.isEnum = true;
}
this.typename = name;
}
public AnnotationVisitor visitAnnotation(String classDesc, boolean isRuntime) {
return null;
}
public void visitAttribute(Attribute attribute) {
}
public void visitInnerClass(String name, String outername, String innerName, int access) {
if (name.equals(typename)) {
this.flags = access;
}
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
fields.add(new FieldMember(typename, access, name, desc, signature));
if (name.equals("$callSiteArray")) {
isGroovy = true;
}
return null;
}
/**
* Visit a method in the class and build an appropriate representation for it to include in the extracted
* output.
*/
public MethodVisitor visitMethod(int flags, String name, String descriptor, String genericSignature,
String[] exceptions) {
if (name.charAt(0) != '<') {
methods.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
}
else {
if (name.equals("<init>")) {
//Even though constructors are not reloadable at present, we need to add them to type descriptors to know
//about their original modifiers (these are promoted to public to allow executors access to them).
constructors.add(new MethodMember(flags, name, descriptor, genericSignature, exceptions));
}
else if (name.equals("<clinit>")) {
hasClinit = true;
}
}
return null;
}
public void visitOuterClass(String owner, String name, String desc) {
}
public void visitSource(String source, String debug) {
}
public void visitEnd() {
}
}
}

View File

@@ -1,35 +1,35 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Similar to the AspectJ type pattern model - used for defining reloadable type inclusions/exclusions.
*
* @author Andy Clement
* @since 0.5.0
*/
public abstract class TypePattern {
public boolean matches(String dottedname) {
if (GlobalConfiguration.assertsMode) {
Utils.assertDotted(dottedname);
}
return internalMatches(dottedname);
}
protected abstract boolean internalMatches(String input);
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded;
/**
* Similar to the AspectJ type pattern model - used for defining reloadable type inclusions/exclusions.
*
* @author Andy Clement
* @since 0.5.0
*/
public abstract class TypePattern {
public boolean matches(String dottedname) {
if (GlobalConfiguration.assertsMode) {
Utils.assertDotted(dottedname);
}
return internalMatches(dottedname);
}
protected abstract boolean internalMatches(String input);
}

View File

@@ -1,120 +1,120 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
/**
* Class pre-processor.
*
* @author Andy Clement
* @since 0.5.0
*/
public class ClassPreProcessorAgentAdapter implements ClassFileTransformer {
private static Logger log = Logger.getLogger(ClassPreProcessorAgentAdapter.class.getName());
private static SpringLoadedPreProcessor preProcessor;
private static ClassPreProcessorAgentAdapter instance;
public ClassPreProcessorAgentAdapter() {
instance = this;
}
static {
try {
preProcessor = new SpringLoadedPreProcessor();
preProcessor.initialize();
}
catch (Exception e) {
throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString());
}
}
/**
* @param loader the defining class loader
* @param className the name of class being loaded
* @param classBeingRedefined when hotswap is called
* @param protectionDomain the ProtectionDomain for the class represented by the bytes
* @param bytes the bytecode before weaving
* @return the weaved bytecode
*/
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] bytes) throws IllegalClassFormatException {
try {
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("> (loader=" + loader + " className=" + className + ", classBeingRedefined="
+ classBeingRedefined
+ ", protectedDomain=" + (protectionDomain != null) + ", bytes= "
+ (bytes == null ? "null" : bytes.length));
}
// TODO determine if this is the right behaviour for hot code replace:
// Handling class redefinition (hot code replace) - what to do depends on whether the type is a reloadable type or not
// If reloadable - return the class as originally defined, and treat this new input data as the new version to make live
// If not-reloadable - rewrite the call sites and attempt hot code replace
if (classBeingRedefined != null) {
// pretend no-one attempted the reload by returning original bytes. The 'watcher' for the class
// should see the changes and pick them up. Should we force it here?
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(loader);
if (typeRegistry == null) {
return null;
}
boolean isRTN = typeRegistry.isReloadableTypeName(className);
if (isRTN) {
ReloadableType rtype = typeRegistry.getReloadableType(className, false);
// CurrentLiveVersion clv = rtype.getLiveVersion();
// String suffix = "0";
// if (clv != null) {
// suffix = clv.getVersionStamp() + "H";
// }
// rtype.loadNewVersion(suffix, bytes);
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("Tricking HCR for " + className);
}
return rtype.bytesLoaded; // returning original bytes
}
return null;
}
// System.err.println("transform(" + loader.getClass().getName() + ",classname=" + className +
// ",classBeingRedefined=" + classBeingRedefined + ",protectionDomain=" + protectionDomain + ")");
return preProcessor.preProcess(loader, className, protectionDomain, bytes);
}
catch (Throwable t) {
new RuntimeException("Reloading agent exited via exception, please raise a jira", t).printStackTrace();
return bytes;
}
}
public static void reload(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
instance.transform(loader, className, classBeingRedefined, protectionDomain, bytes);
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
/**
* Class pre-processor.
*
* @author Andy Clement
* @since 0.5.0
*/
public class ClassPreProcessorAgentAdapter implements ClassFileTransformer {
private static Logger log = Logger.getLogger(ClassPreProcessorAgentAdapter.class.getName());
private static SpringLoadedPreProcessor preProcessor;
private static ClassPreProcessorAgentAdapter instance;
public ClassPreProcessorAgentAdapter() {
instance = this;
}
static {
try {
preProcessor = new SpringLoadedPreProcessor();
preProcessor.initialize();
}
catch (Exception e) {
throw new ExceptionInInitializerError("could not initialize JSR163 preprocessor due to: " + e.toString());
}
}
/**
* @param loader the defining class loader
* @param className the name of class being loaded
* @param classBeingRedefined when hotswap is called
* @param protectionDomain the ProtectionDomain for the class represented by the bytes
* @param bytes the bytecode before weaving
* @return the weaved bytecode
*/
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] bytes) throws IllegalClassFormatException {
try {
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("> (loader=" + loader + " className=" + className + ", classBeingRedefined="
+ classBeingRedefined
+ ", protectedDomain=" + (protectionDomain != null) + ", bytes= "
+ (bytes == null ? "null" : bytes.length));
}
// TODO determine if this is the right behaviour for hot code replace:
// Handling class redefinition (hot code replace) - what to do depends on whether the type is a reloadable type or not
// If reloadable - return the class as originally defined, and treat this new input data as the new version to make live
// If not-reloadable - rewrite the call sites and attempt hot code replace
if (classBeingRedefined != null) {
// pretend no-one attempted the reload by returning original bytes. The 'watcher' for the class
// should see the changes and pick them up. Should we force it here?
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(loader);
if (typeRegistry == null) {
return null;
}
boolean isRTN = typeRegistry.isReloadableTypeName(className);
if (isRTN) {
ReloadableType rtype = typeRegistry.getReloadableType(className, false);
// CurrentLiveVersion clv = rtype.getLiveVersion();
// String suffix = "0";
// if (clv != null) {
// suffix = clv.getVersionStamp() + "H";
// }
// rtype.loadNewVersion(suffix, bytes);
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info("Tricking HCR for " + className);
}
return rtype.bytesLoaded; // returning original bytes
}
return null;
}
// System.err.println("transform(" + loader.getClass().getName() + ",classname=" + className +
// ",classBeingRedefined=" + classBeingRedefined + ",protectionDomain=" + protectionDomain + ")");
return preProcessor.preProcess(loader, className, protectionDomain, bytes);
}
catch (Throwable t) {
new RuntimeException("Reloading agent exited via exception, please raise a jira", t).printStackTrace();
return bytes;
}
}
public static void reload(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
instance.transform(loader, className, classBeingRedefined, protectionDomain, bytes);
}
}

View File

@@ -1,294 +1,294 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springsource.loaded.FileChangeListener;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.TypeRegistry;
/**
* A simple watcher for the file system. Uses a thread to keep an eye on a number of files and calls back registered
* interested parties when a change is observed. The thread only starts when there is something to watch. The thread is
* given a name indicating the classloader for which it is watching files. Once it starts to watch files the name will
* be enhanced to indicate how many.
*
* @author Andy Clement
* @since 0.5.0
*/
public class FileSystemWatcher {
// the thread being managed
private Thread thread;
// whether the thread is running
private boolean threadRunning = false;
// The Watcher running inside the thread
private Watcher watchThread;
public FileSystemWatcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
watchThread = new Watcher(listener, typeRegistryId, classloadername);
}
/**
* Start the thread if it isn't already started.
*/
private void ensureWatchThreadRunning() {
if (!threadRunning) {
thread = new Thread(watchThread);
thread.setDaemon(true);
thread.start();
watchThread.setThread(thread);
watchThread.updateName();
threadRunning = true;
}
}
/**
* Shutdown the thread.
*/
public void shutdown() {
if (threadRunning) {
watchThread.timeToStop();
}
}
/**
* Add a new file to the list of those being monitored. If the file is something that can be watched, then this
* method will cause the thread to start (if it hasn't already been started).
*
* @param fileToMonitor the file to start monitor
*/
public void register(File fileToMonitor) {
if (watchThread.addFile(fileToMonitor)) {
ensureWatchThreadRunning();
watchThread.updateName();
}
}
/**
* Enables the filesystem watching to be paused/unpaused.
*
* @param shouldBePaused watching should be paused?
*/
public void setPaused(boolean shouldBePaused) {
watchThread.paused = shouldBePaused;
}
}
class Watcher implements Runnable {
private static Logger log = Logger.getLogger(Watcher.class.getName());
long lastScanTime;
// TODO configurable scan interval?
private static long interval = 1100;// ms
List<File> watchListFiles = new ArrayList<File>();
List<Long> watchListLMTs = new ArrayList<Long>();
FileChangeListener listener;
private boolean timeToStop = false;
public boolean paused = false;
private Thread thread = null;
private int typeRegistryId;
private String classloadername;
private int registryLivenessCount = 0;
private static int registryLivenessCountInterval = 300;
public Watcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
this.listener = listener;
this.typeRegistryId = typeRegistryId;
this.classloadername = classloadername;
}
public void setThread(Thread thread) {
this.thread = thread;
}
/**
* Add a new File that the thread should start watching. If the file does not exist nothing happens (this may be
* because a class has been generated on the fly and really there is nothing to watch on disk).
*
* @param fileToWatch the new file to watch
* @return true if the file is now being watched, false otherwise
*/
public boolean addFile(File fileToWatch) {
if (!fileToWatch.exists()) {
return false;
}
synchronized (this) {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Now watching " + fileToWatch);
}
int insertionPos = findPosition(fileToWatch);
if (insertionPos == -1) {
watchListFiles.add(fileToWatch);
watchListLMTs.add(fileToWatch.lastModified());
}
else {
watchListFiles.add(insertionPos, fileToWatch);
watchListLMTs.add(insertionPos, fileToWatch.lastModified());
}
return true;
}
}
public void updateName() {
if (thread != null) {
thread.setName("FileSystemWatcher: files=#" + watchListFiles.size() + " cl=" + classloadername);
}
}
private int findPosition(File file) {
String filename = file.getName();
int len = watchListFiles.size();
if (len == 0) {
return 0;
}
for (int f = 0; f < len; f++) {
File file2 = watchListFiles.get(f);
int cmp = file2.getName().compareTo(filename);
// as we are using 'names' we are only considering the last part, so foo/bar/Goo.class and foo/Goo.class look the same
// and will return cmp==0. Not really sure it matters about using fq names
if (cmp > 0) {
return f;
}
else if (GlobalConfiguration.assertsMode && cmp == 0) {
// Are we watching the same file twice, that is bad!
if (file2.getAbsoluteFile().toString().equals(file.getAbsoluteFile().toString())) {
log.severe("Watching the same file twice: " + file.getAbsoluteFile().toString());
}
}
}
return -1;
}
public void run() {
while (!timeToStop) {
registryLivenessCount++;
if ((registryLivenessCount % registryLivenessCountInterval) == 0) {
// Time to check if the registry is still alive!
if (!TypeRegistry.typeRegistryExistsForId(typeRegistryId)) {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("TypeRegistry " + typeRegistryId + " gone, no point in thread continuing!");
}
return;
}
registryLivenessCount = 0;
}
try {
Thread.sleep(interval);
}
catch (Exception e) {
}
if (!paused) {
List<File> changedFiles = new ArrayList<File>();
synchronized (this) {
int len = watchListFiles.size();
for (int f = 0; f < len; f++) {
File file = watchListFiles.get(f);
long lastModTime = file.lastModified();
if (lastModTime > watchListLMTs.get(f)) {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Observed last modification time change for " + file + " (lastScanTime="
+ lastScanTime + ")");
}
watchListLMTs.set(f, lastModTime);
changedFiles.add(file);
}
}
lastScanTime = System.currentTimeMillis();
}
for (File changedFile : changedFiles) {
determineChangesSince(changedFile, lastScanTime);
}
}
}
}
/*
* problem is that we check some file X, it hasn't changed - we then take longer than interval to check all the
* other files we are watching.
*/
private void determineChangesSince(File file, long lastScanTime) {
try {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Firing file changed event " + file);
}
listener.fileChanged(file);
if (file.isDirectory()) {
File[] filesOfInterest = file.listFiles(new RecentChangeFilter(lastScanTime));
for (File f : filesOfInterest) {
if (f.isDirectory()) {
determineChangesSince(f, lastScanTime);
}
else {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Observed last modification time change for " + f + " (lastScanTime="
+ lastScanTime + ")");
log.info("Firing file changed event " + file);
}
listener.fileChanged(f);
}
}
}
}
catch (Throwable t) {
if (log.isLoggable(Level.SEVERE)) {
log.log(Level.SEVERE, "FileWatcher caught serious error, see cause", t);
}
}
}
static class RecentChangeFilter implements FileFilter {
private long lastScanTime;
public RecentChangeFilter(long lastScanTime) {
this.lastScanTime = lastScanTime;
}
public boolean accept(File pathname) {
return (pathname.lastModified() > lastScanTime);
}
}
public void timeToStop() {
timeToStop = true;
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springsource.loaded.FileChangeListener;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.TypeRegistry;
/**
* A simple watcher for the file system. Uses a thread to keep an eye on a number of files and calls back registered
* interested parties when a change is observed. The thread only starts when there is something to watch. The thread is
* given a name indicating the classloader for which it is watching files. Once it starts to watch files the name will
* be enhanced to indicate how many.
*
* @author Andy Clement
* @since 0.5.0
*/
public class FileSystemWatcher {
// the thread being managed
private Thread thread;
// whether the thread is running
private boolean threadRunning = false;
// The Watcher running inside the thread
private Watcher watchThread;
public FileSystemWatcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
watchThread = new Watcher(listener, typeRegistryId, classloadername);
}
/**
* Start the thread if it isn't already started.
*/
private void ensureWatchThreadRunning() {
if (!threadRunning) {
thread = new Thread(watchThread);
thread.setDaemon(true);
thread.start();
watchThread.setThread(thread);
watchThread.updateName();
threadRunning = true;
}
}
/**
* Shutdown the thread.
*/
public void shutdown() {
if (threadRunning) {
watchThread.timeToStop();
}
}
/**
* Add a new file to the list of those being monitored. If the file is something that can be watched, then this
* method will cause the thread to start (if it hasn't already been started).
*
* @param fileToMonitor the file to start monitor
*/
public void register(File fileToMonitor) {
if (watchThread.addFile(fileToMonitor)) {
ensureWatchThreadRunning();
watchThread.updateName();
}
}
/**
* Enables the filesystem watching to be paused/unpaused.
*
* @param shouldBePaused watching should be paused?
*/
public void setPaused(boolean shouldBePaused) {
watchThread.paused = shouldBePaused;
}
}
class Watcher implements Runnable {
private static Logger log = Logger.getLogger(Watcher.class.getName());
long lastScanTime;
// TODO configurable scan interval?
private static long interval = 1100;// ms
List<File> watchListFiles = new ArrayList<File>();
List<Long> watchListLMTs = new ArrayList<Long>();
FileChangeListener listener;
private boolean timeToStop = false;
public boolean paused = false;
private Thread thread = null;
private int typeRegistryId;
private String classloadername;
private int registryLivenessCount = 0;
private static int registryLivenessCountInterval = 300;
public Watcher(FileChangeListener listener, int typeRegistryId, String classloadername) {
this.listener = listener;
this.typeRegistryId = typeRegistryId;
this.classloadername = classloadername;
}
public void setThread(Thread thread) {
this.thread = thread;
}
/**
* Add a new File that the thread should start watching. If the file does not exist nothing happens (this may be
* because a class has been generated on the fly and really there is nothing to watch on disk).
*
* @param fileToWatch the new file to watch
* @return true if the file is now being watched, false otherwise
*/
public boolean addFile(File fileToWatch) {
if (!fileToWatch.exists()) {
return false;
}
synchronized (this) {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Now watching " + fileToWatch);
}
int insertionPos = findPosition(fileToWatch);
if (insertionPos == -1) {
watchListFiles.add(fileToWatch);
watchListLMTs.add(fileToWatch.lastModified());
}
else {
watchListFiles.add(insertionPos, fileToWatch);
watchListLMTs.add(insertionPos, fileToWatch.lastModified());
}
return true;
}
}
public void updateName() {
if (thread != null) {
thread.setName("FileSystemWatcher: files=#" + watchListFiles.size() + " cl=" + classloadername);
}
}
private int findPosition(File file) {
String filename = file.getName();
int len = watchListFiles.size();
if (len == 0) {
return 0;
}
for (int f = 0; f < len; f++) {
File file2 = watchListFiles.get(f);
int cmp = file2.getName().compareTo(filename);
// as we are using 'names' we are only considering the last part, so foo/bar/Goo.class and foo/Goo.class look the same
// and will return cmp==0. Not really sure it matters about using fq names
if (cmp > 0) {
return f;
}
else if (GlobalConfiguration.assertsMode && cmp == 0) {
// Are we watching the same file twice, that is bad!
if (file2.getAbsoluteFile().toString().equals(file.getAbsoluteFile().toString())) {
log.severe("Watching the same file twice: " + file.getAbsoluteFile().toString());
}
}
}
return -1;
}
public void run() {
while (!timeToStop) {
registryLivenessCount++;
if ((registryLivenessCount % registryLivenessCountInterval) == 0) {
// Time to check if the registry is still alive!
if (!TypeRegistry.typeRegistryExistsForId(typeRegistryId)) {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("TypeRegistry " + typeRegistryId + " gone, no point in thread continuing!");
}
return;
}
registryLivenessCount = 0;
}
try {
Thread.sleep(interval);
}
catch (Exception e) {
}
if (!paused) {
List<File> changedFiles = new ArrayList<File>();
synchronized (this) {
int len = watchListFiles.size();
for (int f = 0; f < len; f++) {
File file = watchListFiles.get(f);
long lastModTime = file.lastModified();
if (lastModTime > watchListLMTs.get(f)) {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Observed last modification time change for " + file + " (lastScanTime="
+ lastScanTime + ")");
}
watchListLMTs.set(f, lastModTime);
changedFiles.add(file);
}
}
lastScanTime = System.currentTimeMillis();
}
for (File changedFile : changedFiles) {
determineChangesSince(changedFile, lastScanTime);
}
}
}
}
/*
* problem is that we check some file X, it hasn't changed - we then take longer than interval to check all the
* other files we are watching.
*/
private void determineChangesSince(File file, long lastScanTime) {
try {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Firing file changed event " + file);
}
listener.fileChanged(file);
if (file.isDirectory()) {
File[] filesOfInterest = file.listFiles(new RecentChangeFilter(lastScanTime));
for (File f : filesOfInterest) {
if (f.isDirectory()) {
determineChangesSince(f, lastScanTime);
}
else {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Observed last modification time change for " + f + " (lastScanTime="
+ lastScanTime + ")");
log.info("Firing file changed event " + file);
}
listener.fileChanged(f);
}
}
}
}
catch (Throwable t) {
if (log.isLoggable(Level.SEVERE)) {
log.log(Level.SEVERE, "FileWatcher caught serious error, see cause", t);
}
}
}
static class RecentChangeFilter implements FileFilter {
private long lastScanTime;
public RecentChangeFilter(long lastScanTime) {
this.lastScanTime = lastScanTime;
}
public boolean accept(File pathname) {
return (pathname.lastModified() > lastScanTime);
}
}
public void timeToStop() {
timeToStop = true;
}
}

View File

@@ -1,60 +1,60 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springsource.loaded.FileChangeListener;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
/**
*
* @author Andy Clement
* @since 0.5.0
*/
public class ReloadableFileChangeListener implements FileChangeListener {
private static Logger log = Logger.getLogger(ReloadableFileChangeListener.class.getName());
private TypeRegistry typeRegistry;
private Map<File, ReloadableType> correspondingReloadableTypes = new HashMap<File, ReloadableType>();
public ReloadableFileChangeListener(TypeRegistry typeRegistry) {
this.typeRegistry = typeRegistry;
}
public void fileChanged(File file) {
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info(" processing change for " + file);
}
ReloadableType rtype = correspondingReloadableTypes.get(file);
typeRegistry.loadNewVersion(rtype, file);
}
public void register(ReloadableType rtype, File file) {
correspondingReloadableTypes.put(file, rtype);
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springsource.loaded.FileChangeListener;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
/**
*
* @author Andy Clement
* @since 0.5.0
*/
public class ReloadableFileChangeListener implements FileChangeListener {
private static Logger log = Logger.getLogger(ReloadableFileChangeListener.class.getName());
private TypeRegistry typeRegistry;
private Map<File, ReloadableType> correspondingReloadableTypes = new HashMap<File, ReloadableType>();
public ReloadableFileChangeListener(TypeRegistry typeRegistry) {
this.typeRegistry = typeRegistry;
}
public void fileChanged(File file) {
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
log.info(" processing change for " + file);
}
ReloadableType rtype = correspondingReloadableTypes.get(file);
typeRegistry.loadNewVersion(rtype, file);
}
public void register(ReloadableType rtype, File file) {
correspondingReloadableTypes.put(file, rtype);
}
}

View File

@@ -1,62 +1,62 @@
/*
* Copyright 2010-2014 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
/**
* Basic agent implementation. This agent is declared in the META-INF/MANIFEST.MF file - that is how it is 'plugged in'
* to the JVM when '-javaagent:springloaded.jar' is used.
*
* @author Andy Clement
* @since 0.5.0
*/
public class SpringLoadedAgent {
private static ClassFileTransformer transformer = new ClassPreProcessorAgentAdapter();
private static Instrumentation instrumentation;
public static void premain(String options, Instrumentation inst) {
// Handle duplicate agents
if (instrumentation != null) {
return;
}
instrumentation = inst;
instrumentation.addTransformer(transformer);
}
public static void agentmain(String options, Instrumentation inst) {
if (instrumentation != null) {
return;
}
instrumentation = inst;
instrumentation.addTransformer(transformer);
}
/**
* @return the Instrumentation instance
*/
public static Instrumentation getInstrumentation() {
if (instrumentation == null) {
throw new UnsupportedOperationException("Java 5 was not started with preMain -javaagent for SpringLoaded");
}
return instrumentation;
}
}
/*
* Copyright 2010-2014 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.agent;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
/**
* Basic agent implementation. This agent is declared in the META-INF/MANIFEST.MF file - that is how it is 'plugged in'
* to the JVM when '-javaagent:springloaded.jar' is used.
*
* @author Andy Clement
* @since 0.5.0
*/
public class SpringLoadedAgent {
private static ClassFileTransformer transformer = new ClassPreProcessorAgentAdapter();
private static Instrumentation instrumentation;
public static void premain(String options, Instrumentation inst) {
// Handle duplicate agents
if (instrumentation != null) {
return;
}
instrumentation = inst;
instrumentation.addTransformer(transformer);
}
public static void agentmain(String options, Instrumentation inst) {
if (instrumentation != null) {
return;
}
instrumentation = inst;
instrumentation.addTransformer(transformer);
}
/**
* @return the Instrumentation instance
*/
public static Instrumentation getInstrumentation() {
if (instrumentation == null) {
throw new UnsupportedOperationException("Java 5 was not started with preMain -javaagent for SpringLoaded");
}
return instrumentation;
}
}

View File

@@ -1,281 +1,281 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.File;
import java.io.FileInputStream;
import java.io.PrintStream;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils;
/**
* @author Andy Clement
*/
public class ClassPrinter extends ClassVisitor implements Opcodes {
private PrintStream destination;
private boolean includeBytecode;
private int includeFlags = 0x0000;
public final static int INCLUDE_BYTECODE = 0x0001;
public final static int INCLUDE_LINE_NUMBERS = 0x0002;
public static void main(String[] argv) throws Exception {
ClassReader reader = new ClassReader(Utils.loadBytesFromStream(new FileInputStream(new File(argv[0]))));
reader.accept(new ClassPrinter(System.out, INCLUDE_BYTECODE), 0);
}
public ClassPrinter(PrintStream destination) {
this(destination, INCLUDE_BYTECODE);
}
public ClassPrinter(PrintStream destination, int includeFlags) {
super(ASM5);
this.destination = destination;
this.includeFlags = includeFlags;
}
public static void print(String message, byte[] bytes) {
System.out.println(message);
print(bytes, true);
}
public static void print(byte[] bytes) {
print(bytes, true);
}
public static void print(byte[] bytes, int includeFlags) {
ClassReader reader = new ClassReader(bytes);
reader.accept(new ClassPrinter(System.out, includeFlags), 0);
}
public static void print(byte[] bytes, boolean includeBytecode) {
ClassReader reader = new ClassReader(bytes);
reader.accept(new ClassPrinter(System.out, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
}
public static void print(PrintStream printStream, byte[] bytes, boolean includeBytecode) {
ClassReader reader = new ClassReader(bytes);
reader.accept(new ClassPrinter(printStream, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
}
public static void print(String message, byte[] bytes, boolean includeBytecode) {
System.out.println(message);
print(bytes, includeBytecode);
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
destination.println("CLASS: " + name + " v" + Integer.toString(version) + " " + toHex(access, 4) + "("
+ toAccessForClass(access) + ") super " + superName
+ (interfaces == null || interfaces.length == 0 ? "" : " interfaces" + toString(interfaces)));
}
private String toString(Object[] os) {
if (os == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Object o : os) {
sb.append(o).append(" ");
}
return sb.toString();
}
private String toAccessForClass(int flags) {
StringBuilder sb = new StringBuilder();
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
sb.append("public ");
}
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
sb.append("private ");
}
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
sb.append("protected ");
}
if ((flags & Opcodes.ACC_STATIC) != 0) {
sb.append("static ");
}
if ((flags & Opcodes.ACC_FINAL) != 0) {
sb.append("final ");
}
if ((flags & Opcodes.ACC_SYNCHRONIZED) != 0) {
sb.append("synchronized ");
}
if ((flags & Opcodes.ACC_BRIDGE) != 0) {
sb.append("bridge ");
}
if ((flags & Opcodes.ACC_VARARGS) != 0) {
sb.append("varargs ");
}
if ((flags & Opcodes.ACC_NATIVE) != 0) {
sb.append("native ");
}
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
sb.append("abstract ");
}
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
sb.append("synthetic ");
}
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
sb.append("deprecated ");
}
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
sb.append("interface ");
}
return sb.toString().trim();
}
public static String toAccessForMember(int flags) {
StringBuilder sb = new StringBuilder();
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
sb.append("public ");
}
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
sb.append("private ");
}
if ((flags & Opcodes.ACC_STATIC) != 0) {
sb.append("static ");
}
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
sb.append("protected ");
}
if ((flags & Opcodes.ACC_FINAL) != 0) {
sb.append("final ");
}
if ((flags & Opcodes.ACC_SUPER) != 0) {
sb.append("super ");
}
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
sb.append("interface ");
}
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
sb.append("abstract ");
}
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
sb.append("synthetic ");
}
if ((flags & Opcodes.ACC_ANNOTATION) != 0) {
sb.append("annotation ");
}
if ((flags & Opcodes.ACC_ENUM) != 0) {
sb.append("enum ");
}
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
sb.append("deprecated ");
}
return sb.toString().trim();
}
private String toHex(int i, int len) {
StringBuilder sb = new StringBuilder("00000000");
sb.append(Integer.toHexString(i));
return "0x" + sb.substring(sb.length() - len);
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
destination.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
return new AnnotationVisitorPrinter();
}
class AnnotationVisitorPrinter extends AnnotationVisitor {
public AnnotationVisitorPrinter() {
super(ASM5);
}
public void visit(String name, Object value) {
destination.print(name + "=" + value + " ");
}
public void visitEnum(String name, String desc, String value) {
destination.print(name + "=" + desc + "." + value + " ");
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
destination.print(name + "=" + desc + " ");
return new AnnotationVisitorPrinter();
}
public AnnotationVisitor visitArray(String name) {
destination.print(name + " ");
return new AnnotationVisitorPrinter();
}
public void visitEnd() {
destination.println();
}
}
public void visitAttribute(Attribute attr) {
}
public void visitEnd() {
destination.println();
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
StringBuilder sb = new StringBuilder();
sb.append("FIELD " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + " " + desc
+ (signature != null ? " " + signature : ""));
destination.println(sb.toString().trim());
return null;
}
public void visitInnerClass(String name, String outerName, String innerName, int access) {
destination.println("INNERCLASS: " + name + " " + outerName + " " + innerName + " " + access);
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
StringBuilder s = new StringBuilder();
s.append("METHOD: " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + desc + " "
+ fromArray(exceptions));
destination.println(s.toString().trim());
return (includeFlags & INCLUDE_BYTECODE) != 0 ? new MethodPrinter(destination, includeFlags) : null;
}
private String fromArray(Object[] os) {
if (os == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Object o : os) {
sb.append(o).append(" ");
}
return sb.toString();
}
public void visitOuterClass(String owner, String name, String desc) {
destination.println("OUTERCLASS: " + owner + " " + name + " " + desc);
}
public void visitSource(String source, String debug) {
destination.println("SOURCE: " + source + " " + debug);
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.File;
import java.io.FileInputStream;
import java.io.PrintStream;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils;
/**
* @author Andy Clement
*/
public class ClassPrinter extends ClassVisitor implements Opcodes {
private PrintStream destination;
private boolean includeBytecode;
private int includeFlags = 0x0000;
public final static int INCLUDE_BYTECODE = 0x0001;
public final static int INCLUDE_LINE_NUMBERS = 0x0002;
public static void main(String[] argv) throws Exception {
ClassReader reader = new ClassReader(Utils.loadBytesFromStream(new FileInputStream(new File(argv[0]))));
reader.accept(new ClassPrinter(System.out, INCLUDE_BYTECODE), 0);
}
public ClassPrinter(PrintStream destination) {
this(destination, INCLUDE_BYTECODE);
}
public ClassPrinter(PrintStream destination, int includeFlags) {
super(ASM5);
this.destination = destination;
this.includeFlags = includeFlags;
}
public static void print(String message, byte[] bytes) {
System.out.println(message);
print(bytes, true);
}
public static void print(byte[] bytes) {
print(bytes, true);
}
public static void print(byte[] bytes, int includeFlags) {
ClassReader reader = new ClassReader(bytes);
reader.accept(new ClassPrinter(System.out, includeFlags), 0);
}
public static void print(byte[] bytes, boolean includeBytecode) {
ClassReader reader = new ClassReader(bytes);
reader.accept(new ClassPrinter(System.out, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
}
public static void print(PrintStream printStream, byte[] bytes, boolean includeBytecode) {
ClassReader reader = new ClassReader(bytes);
reader.accept(new ClassPrinter(printStream, includeBytecode ? INCLUDE_BYTECODE : 0), 0);
}
public static void print(String message, byte[] bytes, boolean includeBytecode) {
System.out.println(message);
print(bytes, includeBytecode);
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
destination.println("CLASS: " + name + " v" + Integer.toString(version) + " " + toHex(access, 4) + "("
+ toAccessForClass(access) + ") super " + superName
+ (interfaces == null || interfaces.length == 0 ? "" : " interfaces" + toString(interfaces)));
}
private String toString(Object[] os) {
if (os == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Object o : os) {
sb.append(o).append(" ");
}
return sb.toString();
}
private String toAccessForClass(int flags) {
StringBuilder sb = new StringBuilder();
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
sb.append("public ");
}
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
sb.append("private ");
}
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
sb.append("protected ");
}
if ((flags & Opcodes.ACC_STATIC) != 0) {
sb.append("static ");
}
if ((flags & Opcodes.ACC_FINAL) != 0) {
sb.append("final ");
}
if ((flags & Opcodes.ACC_SYNCHRONIZED) != 0) {
sb.append("synchronized ");
}
if ((flags & Opcodes.ACC_BRIDGE) != 0) {
sb.append("bridge ");
}
if ((flags & Opcodes.ACC_VARARGS) != 0) {
sb.append("varargs ");
}
if ((flags & Opcodes.ACC_NATIVE) != 0) {
sb.append("native ");
}
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
sb.append("abstract ");
}
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
sb.append("synthetic ");
}
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
sb.append("deprecated ");
}
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
sb.append("interface ");
}
return sb.toString().trim();
}
public static String toAccessForMember(int flags) {
StringBuilder sb = new StringBuilder();
if ((flags & Opcodes.ACC_PUBLIC) != 0) {
sb.append("public ");
}
if ((flags & Opcodes.ACC_PRIVATE) != 0) {
sb.append("private ");
}
if ((flags & Opcodes.ACC_STATIC) != 0) {
sb.append("static ");
}
if ((flags & Opcodes.ACC_PROTECTED) != 0) {
sb.append("protected ");
}
if ((flags & Opcodes.ACC_FINAL) != 0) {
sb.append("final ");
}
if ((flags & Opcodes.ACC_SUPER) != 0) {
sb.append("super ");
}
if ((flags & Opcodes.ACC_INTERFACE) != 0) {
sb.append("interface ");
}
if ((flags & Opcodes.ACC_ABSTRACT) != 0) {
sb.append("abstract ");
}
if ((flags & Opcodes.ACC_SYNTHETIC) != 0) {
sb.append("synthetic ");
}
if ((flags & Opcodes.ACC_ANNOTATION) != 0) {
sb.append("annotation ");
}
if ((flags & Opcodes.ACC_ENUM) != 0) {
sb.append("enum ");
}
if ((flags & Opcodes.ACC_DEPRECATED) != 0) {
sb.append("deprecated ");
}
return sb.toString().trim();
}
private String toHex(int i, int len) {
StringBuilder sb = new StringBuilder("00000000");
sb.append(Integer.toHexString(i));
return "0x" + sb.substring(sb.length() - len);
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
destination.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
return new AnnotationVisitorPrinter();
}
class AnnotationVisitorPrinter extends AnnotationVisitor {
public AnnotationVisitorPrinter() {
super(ASM5);
}
public void visit(String name, Object value) {
destination.print(name + "=" + value + " ");
}
public void visitEnum(String name, String desc, String value) {
destination.print(name + "=" + desc + "." + value + " ");
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
destination.print(name + "=" + desc + " ");
return new AnnotationVisitorPrinter();
}
public AnnotationVisitor visitArray(String name) {
destination.print(name + " ");
return new AnnotationVisitorPrinter();
}
public void visitEnd() {
destination.println();
}
}
public void visitAttribute(Attribute attr) {
}
public void visitEnd() {
destination.println();
}
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
StringBuilder sb = new StringBuilder();
sb.append("FIELD " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + " " + desc
+ (signature != null ? " " + signature : ""));
destination.println(sb.toString().trim());
return null;
}
public void visitInnerClass(String name, String outerName, String innerName, int access) {
destination.println("INNERCLASS: " + name + " " + outerName + " " + innerName + " " + access);
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
StringBuilder s = new StringBuilder();
s.append("METHOD: " + toHex(access, 4) + "(" + toAccessForMember(access) + ") " + name + desc + " "
+ fromArray(exceptions));
destination.println(s.toString().trim());
return (includeFlags & INCLUDE_BYTECODE) != 0 ? new MethodPrinter(destination, includeFlags) : null;
}
private String fromArray(Object[] os) {
if (os == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Object o : os) {
sb.append(o).append(" ");
}
return sb.toString();
}
public void visitOuterClass(String owner, String name, String desc) {
destination.println("OUTERCLASS: " + owner + " " + name + " " + desc);
}
public void visitSource(String source, String debug) {
destination.println("SOURCE: " + source + " " + debug);
}
}

View File

@@ -1,262 +1,262 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils;
/**
*
* @author Andy Clement
*/
public class MethodPrinter extends MethodVisitor implements Opcodes {
PrintStream to;
int includeFlags;
List<Label> labels = new ArrayList<Label>();
private String toString(Label label) {
int idx = labels.indexOf(label);
if (idx != -1) {
return "L" + idx;
}
labels.add(label);
return "L" + labels.indexOf(label);
}
public MethodPrinter(PrintStream destination) {
this(destination, 0);
}
public MethodPrinter(PrintStream destination, int includeFlags) {
super(ASM5);
this.includeFlags = includeFlags;
this.to = destination;
}
public void visitCode() {
to.print(" CODE\n");
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
to.println(" INVOKEDYNAMIC " + name + "." + desc + " bsm=" + toString(bsm));
}
private String toString(Handle bsm) {
return "#" + bsm.getTag() + " " + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
}
// TODO include 'itf' flag in output (maybe only if true)
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (opcode == Opcodes.INVOKESTATIC) {
to.println(" INVOKESTATIC " + owner + "." + name + desc);
}
else if (opcode == Opcodes.INVOKESPECIAL) {
to.println(" INVOKESPECIAL " + owner + "." + name + desc);
}
else if (opcode == Opcodes.INVOKEVIRTUAL) {
to.println(" INVOKEVIRTUAL " + owner + "." + name + desc);
}
else if (opcode == Opcodes.INVOKEINTERFACE) {
to.println(" INVOKEINTERFACE " + owner + "." + name + desc);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
// -- pas de implemented
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
to.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
return new AnnotationVisitorPrinter();
}
class AnnotationVisitorPrinter extends AnnotationVisitor {
public AnnotationVisitorPrinter() {
super(ASM5);
}
public void visit(String name, Object value) {
to.print(name + "=" + value + " ");
}
public void visitEnum(String name, String desc, String value) {
to.print(name + "=" + desc + "." + value + " ");
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
to.print(name + "=" + desc + " ");
return new AnnotationVisitorPrinter();
}
public AnnotationVisitor visitArray(String name) {
to.print(name + " ");
return new AnnotationVisitorPrinter();
}
public void visitEnd() {
to.println();
}
}
public AnnotationVisitor visitAnnotationDefault() {
return null;
}
public void visitAttribute(Attribute attr) {
to.println(" ATTRIBUTE: " + attr);
}
public void visitEnd() {
}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (opcode == Opcodes.GETSTATIC) {
to.println(" GETSTATIC " + owner + "." + name + " " + desc);
}
else if (opcode == Opcodes.PUTSTATIC) {
to.println(" PUTSTATIC " + owner + "." + name + " " + desc);
}
else if (opcode == Opcodes.GETFIELD) {
to.println(" GETFIELD " + owner + "." + name + " " + desc);
}
else if (opcode == Opcodes.PUTFIELD) {
to.println(" PUTFIELD " + owner + "." + name + " " + desc);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
}
public void visitIincInsn(int var, int increment) {
}
public void visitInsn(int opcode) {
to.println(" " + Utils.toOpcodeString(opcode));
}
public void visitIntInsn(int opcode, int operand) {
to.println(" " + Utils.toOpcodeString(opcode) + " " + operand);
}
public void visitJumpInsn(int opcode, Label label) {
to.println(" " + Utils.toOpcodeString(opcode) + " " + toString(label));
}
public void visitLabel(Label label) {
to.println(" " + toString(label));
}
public void visitLdcInsn(Object cst) {
to.println(" LDC " + cst);
}
public void visitLineNumber(int line, Label start) {
if ((includeFlags & ClassPrinter.INCLUDE_LINE_NUMBERS) != 0) {
to.println(" LINE:" + line);
}
}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
}
public void visitMaxs(int maxStack, int maxLocals) {
}
public void visitMultiANewArrayInsn(String desc, int dims) {
}
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
}
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
}
public void visitTypeInsn(int opcode, String type) {
if (opcode == Opcodes.NEW) { // 187
to.println(" NEW " + type);
}
else if (opcode == Opcodes.ANEWARRAY) { // 189
to.println(" ANEWARRAY " + type);
}
else if (opcode == Opcodes.CHECKCAST) { // 192
to.println(" CHECKCAST " + type);
}
else if (opcode == Opcodes.INSTANCEOF) { // 193
to.println(" INSTANCEOF " + type);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
public void visitVarInsn(int opcode, int var) {
if (opcode == Opcodes.ALOAD) {
to.println(" ALOAD " + var);
}
else if (opcode == Opcodes.ASTORE) {
to.println(" ASTORE " + var);
}
else if (opcode == Opcodes.ILOAD) {
to.println(" ILOAD " + var);
}
else if (opcode == FLOAD) {
to.println(" FLOAD " + var);
}
else if (opcode == LLOAD) {
to.println(" LLOAD " + var);
}
else if (opcode == DLOAD) {
to.println(" DLOAD " + var);
}
else if (opcode == ISTORE) {
to.println(" ISTORE " + var);
}
else if (opcode == LSTORE) {
to.println(" LSTORE " + var);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils;
/**
*
* @author Andy Clement
*/
public class MethodPrinter extends MethodVisitor implements Opcodes {
PrintStream to;
int includeFlags;
List<Label> labels = new ArrayList<Label>();
private String toString(Label label) {
int idx = labels.indexOf(label);
if (idx != -1) {
return "L" + idx;
}
labels.add(label);
return "L" + labels.indexOf(label);
}
public MethodPrinter(PrintStream destination) {
this(destination, 0);
}
public MethodPrinter(PrintStream destination, int includeFlags) {
super(ASM5);
this.includeFlags = includeFlags;
this.to = destination;
}
public void visitCode() {
to.print(" CODE\n");
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
to.println(" INVOKEDYNAMIC " + name + "." + desc + " bsm=" + toString(bsm));
}
private String toString(Handle bsm) {
return "#" + bsm.getTag() + " " + bsm.getOwner() + "." + bsm.getName() + bsm.getDesc();
}
// TODO include 'itf' flag in output (maybe only if true)
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (opcode == Opcodes.INVOKESTATIC) {
to.println(" INVOKESTATIC " + owner + "." + name + desc);
}
else if (opcode == Opcodes.INVOKESPECIAL) {
to.println(" INVOKESPECIAL " + owner + "." + name + desc);
}
else if (opcode == Opcodes.INVOKEVIRTUAL) {
to.println(" INVOKEVIRTUAL " + owner + "." + name + desc);
}
else if (opcode == Opcodes.INVOKEINTERFACE) {
to.println(" INVOKEINTERFACE " + owner + "." + name + desc);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
// -- pas de implemented
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
to.print("ANNOTATION " + desc + " vis?" + visible + " VALUE ");
return new AnnotationVisitorPrinter();
}
class AnnotationVisitorPrinter extends AnnotationVisitor {
public AnnotationVisitorPrinter() {
super(ASM5);
}
public void visit(String name, Object value) {
to.print(name + "=" + value + " ");
}
public void visitEnum(String name, String desc, String value) {
to.print(name + "=" + desc + "." + value + " ");
}
public AnnotationVisitor visitAnnotation(String name, String desc) {
to.print(name + "=" + desc + " ");
return new AnnotationVisitorPrinter();
}
public AnnotationVisitor visitArray(String name) {
to.print(name + " ");
return new AnnotationVisitorPrinter();
}
public void visitEnd() {
to.println();
}
}
public AnnotationVisitor visitAnnotationDefault() {
return null;
}
public void visitAttribute(Attribute attr) {
to.println(" ATTRIBUTE: " + attr);
}
public void visitEnd() {
}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (opcode == Opcodes.GETSTATIC) {
to.println(" GETSTATIC " + owner + "." + name + " " + desc);
}
else if (opcode == Opcodes.PUTSTATIC) {
to.println(" PUTSTATIC " + owner + "." + name + " " + desc);
}
else if (opcode == Opcodes.GETFIELD) {
to.println(" GETFIELD " + owner + "." + name + " " + desc);
}
else if (opcode == Opcodes.PUTFIELD) {
to.println(" PUTFIELD " + owner + "." + name + " " + desc);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
}
public void visitIincInsn(int var, int increment) {
}
public void visitInsn(int opcode) {
to.println(" " + Utils.toOpcodeString(opcode));
}
public void visitIntInsn(int opcode, int operand) {
to.println(" " + Utils.toOpcodeString(opcode) + " " + operand);
}
public void visitJumpInsn(int opcode, Label label) {
to.println(" " + Utils.toOpcodeString(opcode) + " " + toString(label));
}
public void visitLabel(Label label) {
to.println(" " + toString(label));
}
public void visitLdcInsn(Object cst) {
to.println(" LDC " + cst);
}
public void visitLineNumber(int line, Label start) {
if ((includeFlags & ClassPrinter.INCLUDE_LINE_NUMBERS) != 0) {
to.println(" LINE:" + line);
}
}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
}
public void visitMaxs(int maxStack, int maxLocals) {
}
public void visitMultiANewArrayInsn(String desc, int dims) {
}
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
}
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
}
public void visitTypeInsn(int opcode, String type) {
if (opcode == Opcodes.NEW) { // 187
to.println(" NEW " + type);
}
else if (opcode == Opcodes.ANEWARRAY) { // 189
to.println(" ANEWARRAY " + type);
}
else if (opcode == Opcodes.CHECKCAST) { // 192
to.println(" CHECKCAST " + type);
}
else if (opcode == Opcodes.INSTANCEOF) { // 193
to.println(" INSTANCEOF " + type);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
public void visitVarInsn(int opcode, int var) {
if (opcode == Opcodes.ALOAD) {
to.println(" ALOAD " + var);
}
else if (opcode == Opcodes.ASTORE) {
to.println(" ASTORE " + var);
}
else if (opcode == Opcodes.ILOAD) {
to.println(" ILOAD " + var);
}
else if (opcode == FLOAD) {
to.println(" FLOAD " + var);
}
else if (opcode == LLOAD) {
to.println(" LLOAD " + var);
}
else if (opcode == DLOAD) {
to.println(" DLOAD " + var);
}
else if (opcode == ISTORE) {
to.println(" ISTORE " + var);
}
else if (opcode == LSTORE) {
to.println(" LSTORE " + var);
}
else {
throw new IllegalStateException(":" + opcode);
}
}
}

View File

@@ -1,45 +1,45 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.ClassRenamer;
/**
* Tests for renaming of a class.
*
* @author Andy Clement
*/
public class ClassRenamerTests extends SpringLoadedTests {
/**
* Load the byteform of a class, manipulate the bytes to rename it, then try and define it and use it under the new
* name
*/
@Test
public void simpleRename() {
byte[] classbytes = loadBytesForClass("data.Fruity002");
byte[] renamedbytes = ClassRenamer.rename("data.Fruity", classbytes);
Class<?> clazz = loadit("data.Fruity", renamedbytes);
Object value = run(clazz, "getFruit");
Assert.assertEquals("orange", value);
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.ClassRenamer;
/**
* Tests for renaming of a class.
*
* @author Andy Clement
*/
public class ClassRenamerTests extends SpringLoadedTests {
/**
* Load the byteform of a class, manipulate the bytes to rename it, then try and define it and use it under the new
* name
*/
@Test
public void simpleRename() {
byte[] classbytes = loadBytesForClass("data.Fruity002");
byte[] renamedbytes = ClassRenamer.rename("data.Fruity", classbytes);
Class<?> clazz = loadit("data.Fruity", renamedbytes);
Object value = run(clazz, "getFruit");
Assert.assertEquals("orange", value);
}
}

View File

@@ -1,48 +1,48 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
/**
* @author Andy Clement
*/
public class DebuggingTests extends SpringLoadedTests {
@Test
public void rewrite() throws Exception {
TypeRegistry typeRegistry = getTypeRegistry("data.HelloWorld");
ReloadableType rtype = typeRegistry.addType("data.HelloWorld", loadBytesForClass("data.HelloWorld"));
runUnguarded(rtype.getClazz(), "greet");
// Just transform the existing version into a dispatcher/executor
rtype.loadNewVersion("000", rtype.bytesInitial);
Assert.assertEquals("Greet from HelloWorld", runUnguarded(rtype.getClazz(), "greet").stdout);
// Load a real new version
rtype.loadNewVersion("002", retrieveRename("data.HelloWorld", "data.HelloWorld002"));
Assert.assertEquals("Greet from HelloWorld 2", runUnguarded(rtype.getClazz(), "greet").stdout);
// ClassPrinter.print(rtype.getLatestExecutorBytes());
// ClassPrinter.print(rtype.bytesInitial);
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
/**
* @author Andy Clement
*/
public class DebuggingTests extends SpringLoadedTests {
@Test
public void rewrite() throws Exception {
TypeRegistry typeRegistry = getTypeRegistry("data.HelloWorld");
ReloadableType rtype = typeRegistry.addType("data.HelloWorld", loadBytesForClass("data.HelloWorld"));
runUnguarded(rtype.getClazz(), "greet");
// Just transform the existing version into a dispatcher/executor
rtype.loadNewVersion("000", rtype.bytesInitial);
Assert.assertEquals("Greet from HelloWorld", runUnguarded(rtype.getClazz(), "greet").stdout);
// Load a real new version
rtype.loadNewVersion("002", retrieveRename("data.HelloWorld", "data.HelloWorld002"));
Assert.assertEquals("Greet from HelloWorld 2", runUnguarded(rtype.getClazz(), "greet").stdout);
// ClassPrinter.print(rtype.getLatestExecutorBytes());
// ClassPrinter.print(rtype.bytesInitial);
}
}

View File

@@ -1,154 +1,154 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.MethodInvokerRewriter;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.test.infra.Result;
/**
* Tests for creation of the dispatcher instances that forward to the executors
*
* @author Andy Clement
*/
public class DispatcherBuilderTests extends SpringLoadedTests {
@Test
public void reload() throws Exception {
String tclass = "builder.DispatcherTestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
rtype.loadNewVersion("2", rtype.bytesInitial);
// if we made it here, hurrah, we didn't crash - let's call that success!
}
@Test
public void staticMethod() throws Exception {
String tclass = "dispatcher.Staticmethod";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
rtype.loadNewVersion("2", rtype.bytesInitial);
// ClassPrinter.print(rtype.interfaceBytes);
// ClassPrinter.print(rtype.getLatestExecutorBytes());
byte[] callerbytes = loadBytesForClass("dispatcher.StaticmethodCaller");
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
Class<?> callerClazz = loadit("dispatcher.StaticmethodCaller", rewrittenBytes);
result = runUnguarded(callerClazz, "run");
Assert.assertTrue(result.stdout.indexOf("abc") != -1);
}
@Test
public void reloadToString() throws Exception {
String tclass = "builder.DispatcherTestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
Result r = runUnguarded(rtype.getClazz(), "toString");
String firstToString = (String) r.returnValue; // default toString()
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne002"));
r = runUnguarded(rtype.getClazz(), "toString");
String secondToString = (String) r.returnValue;
Assert.assertNotSame(firstToString, secondToString);
Assert.assertEquals("abc", secondToString);
}
/**
* Test we can differentiate between methods that would clash (static method on the target that takes the instance
* as first parameter and an instance method) - these clash methods would normally create
*/
@Test
public void callingClashingMethods() throws Exception {
String tclass = "dispatcher.C";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
rtype.loadNewVersion("2", rtype.bytesInitial);
byte[] callerbytes = loadBytesForClass("dispatcher.CallC");
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
Class<?> callerClazz = loadit("dispatcher.CallC", rewrittenBytes);
result = runUnguarded(callerClazz, "runInstance");
if (result.stdout.indexOf("instance foo running") == -1) {
Assert.fail("Did not find 'instance foo running' in:\n" + result.stdout);
}
result = runUnguarded(callerClazz, "runStatic");
if (result.stdout.indexOf("static foo running") == -1) {
Assert.fail("Did not find 'static foo running' in:\n" + result.stdout);
}
}
// @Test
// public void checkDynamicDispatcher() throws Exception {
// String tclass = "builder.DispatcherTestOne";
// TypeRegistry typeRegistry = getTypeRegistry(tclass);
//
// ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
//
// // Load a version that has a new method in it: foo(I)Ljava/lang/String;
// rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne003"));
//
// // @formatter:off
// Assert.assertEquals(
// // Build a string consisting of the method name and descriptor:
// " ALOAD 3\n"+
//
// // Is it foo(I)Ljava/lang/String; ?
// " LDC foo(I)Ljava/lang/String;\n"+
// " INVOKEVIRTUAL java/lang/String.equals(Ljava/lang/Object;)Z\n"+
// " IFEQ L0\n"+
//
// // It is! So call the real method on the dispatcher after preparing the arguments
// " ALOAD 2\n"+
// " CHECKCAST builder/DispatcherTestOne\n"+
// " ALOAD 1\n"+
// " LDC 0\n"+
// " AALOAD\n"+
// " CHECKCAST java/lang/Integer\n"+
// " INVOKEVIRTUAL java/lang/Integer.intValue()I\n"+
// " INVOKESTATIC builder/DispatcherTestOne__E2.foo(Lbuilder/DispatcherTestOne;I)Ljava/lang/String;\n"+
// " ARETURN\n"+
//
// // Is isnt! So throw an exception
// " L0\n"+
// " NEW java/lang/IllegalStateException\n"+
// " DUP\n"+
// " INVOKESPECIAL java/lang/IllegalStateException.<init>()V\n"+
// " ATHROW\n",
// toStringMethod(rtype.getLatestDispatcherBytes(), Constants.mDynamicDispatchName, false));
// // @formatter:on
// }
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.MethodInvokerRewriter;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.test.infra.Result;
/**
* Tests for creation of the dispatcher instances that forward to the executors
*
* @author Andy Clement
*/
public class DispatcherBuilderTests extends SpringLoadedTests {
@Test
public void reload() throws Exception {
String tclass = "builder.DispatcherTestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
rtype.loadNewVersion("2", rtype.bytesInitial);
// if we made it here, hurrah, we didn't crash - let's call that success!
}
@Test
public void staticMethod() throws Exception {
String tclass = "dispatcher.Staticmethod";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
rtype.loadNewVersion("2", rtype.bytesInitial);
// ClassPrinter.print(rtype.interfaceBytes);
// ClassPrinter.print(rtype.getLatestExecutorBytes());
byte[] callerbytes = loadBytesForClass("dispatcher.StaticmethodCaller");
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
Class<?> callerClazz = loadit("dispatcher.StaticmethodCaller", rewrittenBytes);
result = runUnguarded(callerClazz, "run");
Assert.assertTrue(result.stdout.indexOf("abc") != -1);
}
@Test
public void reloadToString() throws Exception {
String tclass = "builder.DispatcherTestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
Result r = runUnguarded(rtype.getClazz(), "toString");
String firstToString = (String) r.returnValue; // default toString()
// Simply reloads itself to trigger new version handling code paths in both infrastructure and generated code
rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne002"));
r = runUnguarded(rtype.getClazz(), "toString");
String secondToString = (String) r.returnValue;
Assert.assertNotSame(firstToString, secondToString);
Assert.assertEquals("abc", secondToString);
}
/**
* Test we can differentiate between methods that would clash (static method on the target that takes the instance
* as first parameter and an instance method) - these clash methods would normally create
*/
@Test
public void callingClashingMethods() throws Exception {
String tclass = "dispatcher.C";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
rtype.loadNewVersion("2", rtype.bytesInitial);
byte[] callerbytes = loadBytesForClass("dispatcher.CallC");
byte[] rewrittenBytes = MethodInvokerRewriter.rewrite(typeRegistry, callerbytes);
Class<?> callerClazz = loadit("dispatcher.CallC", rewrittenBytes);
result = runUnguarded(callerClazz, "runInstance");
if (result.stdout.indexOf("instance foo running") == -1) {
Assert.fail("Did not find 'instance foo running' in:\n" + result.stdout);
}
result = runUnguarded(callerClazz, "runStatic");
if (result.stdout.indexOf("static foo running") == -1) {
Assert.fail("Did not find 'static foo running' in:\n" + result.stdout);
}
}
// @Test
// public void checkDynamicDispatcher() throws Exception {
// String tclass = "builder.DispatcherTestOne";
// TypeRegistry typeRegistry = getTypeRegistry(tclass);
//
// ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
//
// // Load a version that has a new method in it: foo(I)Ljava/lang/String;
// rtype.loadNewVersion("2", retrieveRename(tclass, "builder.DispatcherTestOne003"));
//
// // @formatter:off
// Assert.assertEquals(
// // Build a string consisting of the method name and descriptor:
// " ALOAD 3\n"+
//
// // Is it foo(I)Ljava/lang/String; ?
// " LDC foo(I)Ljava/lang/String;\n"+
// " INVOKEVIRTUAL java/lang/String.equals(Ljava/lang/Object;)Z\n"+
// " IFEQ L0\n"+
//
// // It is! So call the real method on the dispatcher after preparing the arguments
// " ALOAD 2\n"+
// " CHECKCAST builder/DispatcherTestOne\n"+
// " ALOAD 1\n"+
// " LDC 0\n"+
// " AALOAD\n"+
// " CHECKCAST java/lang/Integer\n"+
// " INVOKEVIRTUAL java/lang/Integer.intValue()I\n"+
// " INVOKESTATIC builder/DispatcherTestOne__E2.foo(Lbuilder/DispatcherTestOne;I)Ljava/lang/String;\n"+
// " ARETURN\n"+
//
// // Is isnt! So throw an exception
// " L0\n"+
// " NEW java/lang/IllegalStateException\n"+
// " DUP\n"+
// " INVOKESPECIAL java/lang/IllegalStateException.<init>()V\n"+
// " ATHROW\n",
// toStringMethod(rtype.getLatestDispatcherBytes(), Constants.mDynamicDispatchName, false));
// // @formatter:on
// }
}

View File

@@ -1,490 +1,490 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.Utils;
import org.springsource.loaded.test.infra.ClassPrinter;
/**
* Tests for creation of the executor instances that run the code
*
* @author Andy Clement
*/
public class ExecutorBuilderTests extends SpringLoadedTests {
/**
* Check properties of the newly created executor.
*/
@Test
public void basicExternals() throws Exception {
String t = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
Class<?> clazz = rtype.getLatestExecutorClass();
Assert.assertEquals(Utils.getExecutorName(t, "37"), clazz.getName());
Assert.assertEquals(3, clazz.getDeclaredMethods().length);
Assert.assertEquals(1, clazz.getDeclaredFields().length);
}
/**
* Check internal structure of the newly created executor.
*/
@Test
public void basicInternalsLocalVariables() throws Exception {
String t = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
checkLocalVariables(rtype.getLatestExecutorBytes(), "foo(Lexecutor/TestOne;Ljava/lang/String;)J",
"thiz:Lexecutor/TestOne;", "s:Ljava/lang/String;");
}
@Test
public void codeStructure() throws Exception {
String tclass = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
// Reload it (triggers creation of dispatcher/executor)
rtype.loadNewVersion("2", rtype.bytesInitial);
// @formatter:off
checkType(
rtype.getLatestExecutorBytes(),
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
+
"SOURCE: TestOne.java null\n"
+
"FIELD 0x0001(public) i I\n"
+
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 0\n"
+
" POP\n"
+
" L1\n"
+
" ALOAD 0\n"
+
" BIPUSH 101\n"
+
" LDC 0\n"
+
" LDC i\n"
+
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
+
" IFEQ L2\n"
+
" INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;\n"
+
" SWAP\n"
+
" DUP_X1\n"
+
" LDC i\n"
+
" INVOKESPECIAL executor/TestOne.r$set(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V\n"
+
" GOTO L3\n" +
" L2\n" +
" PUTFIELD executor/TestOne.i I\n" +
" L3\n" +
" RETURN\n" +
" L4\n" +
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n" +
" CODE\n" +
" L0\n" +
" ALOAD 1\n" +
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
" LRETURN\n" +
" L1\n" +
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n" +
" CODE\n" +
" L0\n" +
" BIPUSH 37\n" +
" IRETURN\n" +
" L1\n" +
"\n");
Assert.assertEquals(
" L0\n" +
" ALOAD 1\n" +
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
" LRETURN\n" +
" L1\n",
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
// @formatter:on
// @formatter:off
Assert.assertEquals(
" L0\n" +
" BIPUSH 37\n" +
" IRETURN\n" +
" L1\n",
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
// @formatter:on
}
@Test
public void secondVersion() throws Exception {
String tclass = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2"));
// testing executor is for second version and not first
// @formatter:off
checkType(
rtype.getLatestExecutorBytes(),
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
+
"SOURCE: TestOne2.java null\n"
+
"FIELD 0x0001(public) i I\n"
+
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 0\n"
+
" POP\n"
+
" RETURN\n"
+
" L1\n"
+
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 1\n"
+
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n"
+
" LRETURN\n"
+
" L1\n"
+
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 0\n"
+
" LDC 0\n"
+
" LDC i\n"
+
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
+
" IFEQ L1\n"
+
" DUP\n"
+
" LDC i\n"
+
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
" CHECKCAST java/lang/Integer\n" +
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
" GOTO L2\n" +
" L1\n" +
" GETFIELD executor/TestOne.i I\n" +
" L2\n" +
" ICONST_2\n" +
" IMUL\n" +
" IRETURN\n" +
" L3\n" +
"\n");
Assert.assertEquals(
" L0\n" +
" ALOAD 1\n" +
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
" LRETURN\n" +
" L1\n",
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
// @formatter:on
//
// @formatter:off
Assert.assertEquals(
" L0\n"
+
" ALOAD 0\n"
+
" LDC 0\n"
+
" LDC i\n"
+
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
+
" IFEQ L1\n"
+
" DUP\n"
+
" LDC i\n"
+
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
" CHECKCAST java/lang/Integer\n" +
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
" GOTO L2\n" +
" L1\n" +
" GETFIELD executor/TestOne.i I\n" +
" L2\n" +
" ICONST_2\n" +
" IMUL\n" +
" IRETURN\n" +
" L3\n",
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
// @formatter:on
}
/**
* Testing that type level annotations are copied to the executor (to answer later reflection questions).
*/
@Test
public void typeLevelAnnotations() {
String t = "executor.A";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "2");
Class<?> clazz = rtype.getLatestExecutorClass();
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
Annotation[] annos = clazz.getAnnotations();
Assert.assertNotNull(annos);
Assert.assertEquals(1, annos.length);
}
/**
* Testing that type level annotations are copied to the executor. This loads a different form of the type with a
* second annotation.
*/
@Test
public void typeLevelAnnotations2() {
String t = "executor.A";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
Class<?> clazz = rtype.getLatestExecutorClass();
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
Annotation[] annos = clazz.getAnnotations();
Assert.assertNotNull(annos);
Assert.assertEquals(2, annos.length);
Set<String> s = new HashSet<String>();
for (Annotation anno : annos) {
s.add(anno.toString());
}
Assert.assertTrue(s.remove("@common.Marker()"));
// Allow for alternate toString() variant
if (!s.remove("@common.Anno(someValue=37, longValue=2, id=abc)")) {
Assert.assertTrue(s.remove("@common.Anno(longValue=2, someValue=37, id=abc)"));
}
Assert.assertEquals(0, s.size());
}
@Test
public void methodLevelAnnotations() throws Exception {
String t = "executor.B";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
checkAnnotations(rtype.bytesLoaded, "m2()V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V", "@common.Marker()");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V");
rtype.loadNewVersion("39", retrieveRename("executor.B", "executor.B2"));
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V", "@common.Marker()",
"@common.Anno(id=abc)");
}
@Test
public void methodLevelAnnotationsOnInterfaces() throws Exception {
String t = "executor.I";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
checkAnnotations(rtype.bytesLoaded, "m2()V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V", "@common.Marker()");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V");
rtype.loadNewVersion("39", retrieveRename("executor.I", "executor.I2"));
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V", "@common.Marker()",
"@common.Anno(id=abc)");
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("m2", rtype.getClazz());
assertEquals("@common.Marker()", m.getAnnotations()[0].toString());
assertIsOneOfThese(printAnnotation(m.getAnnotations()[1]), "@common.Anno(someValue=37, longValue=2, id=abc)",
"@common.Anno(longValue=2, someValue=37, id=abc)");
}
/**
* Check the actual value is one of the possible options.
*/
private void assertIsOneOfThese(String actual, String... possibleValues) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < possibleValues.length; i++) {
if (actual.equals(possibleValues[i])) {
return;
}
buf.append("'" + possibleValues[i] + "'").append("\n");
}
fail("The value:\n'" + actual + "'\n does not match one of these possible options:\n" + buf.toString());
}
//
private String printAnnotation(Annotation a) {
return a.toString();
// StringBuilder buf = new StringBuilder();
// printAnnotationHelper(buf,a);
// return buf.toString();
}
//
// private void printAnnotationHelper(StringBuilder buf, Annotation a) {
// Class<?> clazz = a.annotationType();a.toString()
// clazz.getDeclaredFields()[0].get
// System.out.println(a.annotationType());
//
// }
@Test
public void methodLevelAnnotationsOnInterfaces2() throws Exception {
String t = "reflection.methodannotations.InterfaceTarget";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
checkAnnotations(rtype.bytesLoaded, "privMethod()V", "@reflection.AnnoT3(value=Foo)");
reload(rtype, "37");
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
"@reflection.AnnoT3(value=Foo)");
rtype.loadNewVersion("39", retrieveRename(t, t + "002"));
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
"@reflection.AnnoT3(value=Bar)");
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("privMethod", rtype.getClazz());
assertEquals("@reflection.AnnoT3(value=Bar)", m.getAnnotations()[0].toString());
}
@Test
public void clashingInstanceStaticMethods() throws Exception {
String t = "executor.C";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
}
@Test
public void staticInitializerReloading1() throws Exception {
String t = "clinit.One";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("5", result.returnValue);
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
rtype.runStaticInitializer(); // call is made on reloadable type
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("7", result.returnValue);
}
@Test
public void staticInitializerReloading2() throws Exception {
String t = "clinit.One";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("5", result.returnValue);
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
// use the 'new' ___clinit___ method to drive the static initializer
Method staticInitializer = rtype.getClazz().getMethod("___clinit___");
assertNotNull(staticInitializer);
staticInitializer.invoke(null);
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("7", result.returnValue);
}
/**
* Dealing with final fields
*/
@Test
public void staticInitializerReloading3() throws Exception {
String t = "clinit.Two";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("55", result.returnValue);
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
rtype.runStaticInitializer();
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("99", result.returnValue);
}
/**
* Type that doesn't really have a clinit
*/
@Test
public void staticInitializerReloading4() throws Exception {
String t = "clinit.Three";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("1", result.returnValue);
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
rtype.runStaticInitializer();
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("1", result.returnValue);
rtype.loadNewVersion("3", retrieveRename(t, t + "3"));
rtype.runStaticInitializer();
result = runUnguarded(rtype.getClazz(), "run");
ClassPrinter.print(rtype.getLatestExecutorBytes());
assertEquals("4", result.returnValue);
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.Utils;
import org.springsource.loaded.test.infra.ClassPrinter;
/**
* Tests for creation of the executor instances that run the code
*
* @author Andy Clement
*/
public class ExecutorBuilderTests extends SpringLoadedTests {
/**
* Check properties of the newly created executor.
*/
@Test
public void basicExternals() throws Exception {
String t = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
Class<?> clazz = rtype.getLatestExecutorClass();
Assert.assertEquals(Utils.getExecutorName(t, "37"), clazz.getName());
Assert.assertEquals(3, clazz.getDeclaredMethods().length);
Assert.assertEquals(1, clazz.getDeclaredFields().length);
}
/**
* Check internal structure of the newly created executor.
*/
@Test
public void basicInternalsLocalVariables() throws Exception {
String t = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
checkLocalVariables(rtype.getLatestExecutorBytes(), "foo(Lexecutor/TestOne;Ljava/lang/String;)J",
"thiz:Lexecutor/TestOne;", "s:Ljava/lang/String;");
}
@Test
public void codeStructure() throws Exception {
String tclass = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
// Reload it (triggers creation of dispatcher/executor)
rtype.loadNewVersion("2", rtype.bytesInitial);
// @formatter:off
checkType(
rtype.getLatestExecutorBytes(),
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
+
"SOURCE: TestOne.java null\n"
+
"FIELD 0x0001(public) i I\n"
+
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 0\n"
+
" POP\n"
+
" L1\n"
+
" ALOAD 0\n"
+
" BIPUSH 101\n"
+
" LDC 0\n"
+
" LDC i\n"
+
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
+
" IFEQ L2\n"
+
" INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;\n"
+
" SWAP\n"
+
" DUP_X1\n"
+
" LDC i\n"
+
" INVOKESPECIAL executor/TestOne.r$set(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)V\n"
+
" GOTO L3\n" +
" L2\n" +
" PUTFIELD executor/TestOne.i I\n" +
" L3\n" +
" RETURN\n" +
" L4\n" +
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n" +
" CODE\n" +
" L0\n" +
" ALOAD 1\n" +
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
" LRETURN\n" +
" L1\n" +
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n" +
" CODE\n" +
" L0\n" +
" BIPUSH 37\n" +
" IRETURN\n" +
" L1\n" +
"\n");
Assert.assertEquals(
" L0\n" +
" ALOAD 1\n" +
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
" LRETURN\n" +
" L1\n",
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
// @formatter:on
// @formatter:off
Assert.assertEquals(
" L0\n" +
" BIPUSH 37\n" +
" IRETURN\n" +
" L1\n",
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
// @formatter:on
}
@Test
public void secondVersion() throws Exception {
String tclass = "executor.TestOne";
TypeRegistry typeRegistry = getTypeRegistry(tclass);
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2"));
// testing executor is for second version and not first
// @formatter:off
checkType(
rtype.getLatestExecutorBytes(),
"CLASS: executor/TestOne$$E2 v50 0x0001(public) super java/lang/Object\n"
+
"SOURCE: TestOne2.java null\n"
+
"FIELD 0x0001(public) i I\n"
+
"METHOD: 0x0009(public static) ___init___(Lexecutor/TestOne;)V\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 0\n"
+
" POP\n"
+
" RETURN\n"
+
" L1\n"
+
"METHOD: 0x0009(public static) foo(Lexecutor/TestOne;Ljava/lang/String;)J\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 1\n"
+
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n"
+
" LRETURN\n"
+
" L1\n"
+
"METHOD: 0x0009(public static) hashCode(Lexecutor/TestOne;)I\n"
+
" CODE\n"
+
" L0\n"
+
" ALOAD 0\n"
+
" LDC 0\n"
+
" LDC i\n"
+
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
+
" IFEQ L1\n"
+
" DUP\n"
+
" LDC i\n"
+
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
" CHECKCAST java/lang/Integer\n" +
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
" GOTO L2\n" +
" L1\n" +
" GETFIELD executor/TestOne.i I\n" +
" L2\n" +
" ICONST_2\n" +
" IMUL\n" +
" IRETURN\n" +
" L3\n" +
"\n");
Assert.assertEquals(
" L0\n" +
" ALOAD 1\n" +
" INVOKESTATIC java/lang/Long.parseLong(Ljava/lang/String;)J\n" +
" LRETURN\n" +
" L1\n",
toStringMethod(rtype.getLatestExecutorBytes(), "foo", false));
// @formatter:on
//
// @formatter:off
Assert.assertEquals(
" L0\n"
+
" ALOAD 0\n"
+
" LDC 0\n"
+
" LDC i\n"
+
" INVOKESTATIC org/springsource/loaded/TypeRegistry.instanceFieldInterceptionRequired(ILjava/lang/String;)Z\n"
+
" IFEQ L1\n"
+
" DUP\n"
+
" LDC i\n"
+
" INVOKESPECIAL executor/TestOne.r$get(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
" CHECKCAST java/lang/Integer\n" +
" INVOKEVIRTUAL java/lang/Integer.intValue()I\n" +
" GOTO L2\n" +
" L1\n" +
" GETFIELD executor/TestOne.i I\n" +
" L2\n" +
" ICONST_2\n" +
" IMUL\n" +
" IRETURN\n" +
" L3\n",
toStringMethod(rtype.getLatestExecutorBytes(), "hashCode", false));
// @formatter:on
}
/**
* Testing that type level annotations are copied to the executor (to answer later reflection questions).
*/
@Test
public void typeLevelAnnotations() {
String t = "executor.A";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "2");
Class<?> clazz = rtype.getLatestExecutorClass();
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
Annotation[] annos = clazz.getAnnotations();
Assert.assertNotNull(annos);
Assert.assertEquals(1, annos.length);
}
/**
* Testing that type level annotations are copied to the executor. This loads a different form of the type with a
* second annotation.
*/
@Test
public void typeLevelAnnotations2() {
String t = "executor.A";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
Class<?> clazz = rtype.getLatestExecutorClass();
Assert.assertEquals(Utils.getExecutorName(t, "2"), clazz.getName());
Annotation[] annos = clazz.getAnnotations();
Assert.assertNotNull(annos);
Assert.assertEquals(2, annos.length);
Set<String> s = new HashSet<String>();
for (Annotation anno : annos) {
s.add(anno.toString());
}
Assert.assertTrue(s.remove("@common.Marker()"));
// Allow for alternate toString() variant
if (!s.remove("@common.Anno(someValue=37, longValue=2, id=abc)")) {
Assert.assertTrue(s.remove("@common.Anno(longValue=2, someValue=37, id=abc)"));
}
Assert.assertEquals(0, s.size());
}
@Test
public void methodLevelAnnotations() throws Exception {
String t = "executor.B";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
checkAnnotations(rtype.bytesLoaded, "m2()V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V", "@common.Marker()");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V");
rtype.loadNewVersion("39", retrieveRename("executor.B", "executor.B2"));
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/B;)V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/B;)V", "@common.Marker()",
"@common.Anno(id=abc)");
}
@Test
public void methodLevelAnnotationsOnInterfaces() throws Exception {
String t = "executor.I";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
checkAnnotations(rtype.bytesLoaded, "m()V", "@common.Marker()");
checkAnnotations(rtype.bytesLoaded, "m2()V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V", "@common.Marker()");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V");
rtype.loadNewVersion("39", retrieveRename("executor.I", "executor.I2"));
checkAnnotations(rtype.getLatestExecutorBytes(), "m(Lexecutor/I;)V");
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V", "@common.Marker()",
"@common.Anno(id=abc)");
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("m2", rtype.getClazz());
assertEquals("@common.Marker()", m.getAnnotations()[0].toString());
assertIsOneOfThese(printAnnotation(m.getAnnotations()[1]), "@common.Anno(someValue=37, longValue=2, id=abc)",
"@common.Anno(longValue=2, someValue=37, id=abc)");
}
/**
* Check the actual value is one of the possible options.
*/
private void assertIsOneOfThese(String actual, String... possibleValues) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < possibleValues.length; i++) {
if (actual.equals(possibleValues[i])) {
return;
}
buf.append("'" + possibleValues[i] + "'").append("\n");
}
fail("The value:\n'" + actual + "'\n does not match one of these possible options:\n" + buf.toString());
}
//
private String printAnnotation(Annotation a) {
return a.toString();
// StringBuilder buf = new StringBuilder();
// printAnnotationHelper(buf,a);
// return buf.toString();
}
//
// private void printAnnotationHelper(StringBuilder buf, Annotation a) {
// Class<?> clazz = a.annotationType();a.toString()
// clazz.getDeclaredFields()[0].get
// System.out.println(a.annotationType());
//
// }
@Test
public void methodLevelAnnotationsOnInterfaces2() throws Exception {
String t = "reflection.methodannotations.InterfaceTarget";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
checkAnnotations(rtype.bytesLoaded, "privMethod()V", "@reflection.AnnoT3(value=Foo)");
reload(rtype, "37");
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
"@reflection.AnnoT3(value=Foo)");
rtype.loadNewVersion("39", retrieveRename(t, t + "002"));
checkAnnotations(rtype.getLatestExecutorBytes(), "privMethod(Lreflection/methodannotations/InterfaceTarget;)V",
"@reflection.AnnoT3(value=Bar)");
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("privMethod", rtype.getClazz());
assertEquals("@reflection.AnnoT3(value=Bar)", m.getAnnotations()[0].toString());
}
@Test
public void clashingInstanceStaticMethods() throws Exception {
String t = "executor.C";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
reload(rtype, "37");
}
@Test
public void staticInitializerReloading1() throws Exception {
String t = "clinit.One";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("5", result.returnValue);
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
rtype.runStaticInitializer(); // call is made on reloadable type
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("7", result.returnValue);
}
@Test
public void staticInitializerReloading2() throws Exception {
String t = "clinit.One";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("5", result.returnValue);
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
// use the 'new' ___clinit___ method to drive the static initializer
Method staticInitializer = rtype.getClazz().getMethod("___clinit___");
assertNotNull(staticInitializer);
staticInitializer.invoke(null);
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("7", result.returnValue);
}
/**
* Dealing with final fields
*/
@Test
public void staticInitializerReloading3() throws Exception {
String t = "clinit.Two";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("55", result.returnValue);
rtype.loadNewVersion("39", retrieveRename(t, t + "2"));
rtype.runStaticInitializer();
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("99", result.returnValue);
}
/**
* Type that doesn't really have a clinit
*/
@Test
public void staticInitializerReloading4() throws Exception {
String t = "clinit.Three";
TypeRegistry typeRegistry = getTypeRegistry(t);
ReloadableType rtype = typeRegistry.addType(t, loadBytesForClass(t));
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("1", result.returnValue);
rtype.loadNewVersion("2", retrieveRename(t, t + "2"));
rtype.runStaticInitializer();
result = runUnguarded(rtype.getClazz(), "run");
assertEquals("1", result.returnValue);
rtype.loadNewVersion("3", retrieveRename(t, t + "3"));
rtype.runStaticInitializer();
result = runUnguarded(rtype.getClazz(), "run");
ClassPrinter.print(rtype.getLatestExecutorBytes());
assertEquals("4", result.returnValue);
}
}

View File

@@ -1,212 +1,212 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.springsource.loaded.FileChangeListener;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.agent.FileSystemWatcher;
public class FileSystemWatcherTests {
/**
* Create a folder, watch it then put a couple of files in and check they are detected
*/
@Ignore
@Test
public void dirs() throws IOException {
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
watcher.register(dir);
pause(1000);
create(dir, "abc.txt");
pause(1100);
create(dir, "abcd.txt");
pause(1100);
watcher.shutdown();
Assert.assertTrue(listener.changesDetected.contains("abc.txt"));
Assert.assertTrue(listener.changesDetected.contains("abcd.txt"));
}
@Test
public void files() throws IOException {
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
pause(1000);
File f1 = create(dir, "abc.txt");
watcher.register(f1);
pause(1100);
File f2 = create(dir, "abcd.txt");
watcher.register(f2);
pause(1100);
watcher.setPaused(true);
// Whilst paused, touch both files
touch(f2);
touch(f1);
watcher.setPaused(false);
pause(3000);
watcher.shutdown();
System.out.println(listener.changesDetected);
assertEquals("abc.txt", listener.changesDetected.get(0));
assertEquals("abcd.txt", listener.changesDetected.get(1));
}
@Ignore
@Test
public void innersFirst() throws IOException {
System.out.println("innersFirst");
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
pause(1000);
File f1 = create(dir, "Book$1.class");
watcher.register(f1);
pause(1100);
File f2 = create(dir, "Book.class");
watcher.register(f2);
pause(1100);
File f3 = create(dir, "Book$_2.class");
watcher.register(f3);
pause(1100);
watcher.setPaused(true);
// Whilst paused, touch both files
touch(f3);
touch(f2);
touch(f1);
watcher.setPaused(false);
pause(3000);
System.out.println(listener.changesDetected);
watcher.shutdown();
// Check that inners reported first
assertEquals("Book$1.class", listener.changesDetected.get(0));
assertEquals("Book$_2.class", listener.changesDetected.get(1));
assertEquals("Book.class", listener.changesDetected.get(2));
}
@Ignore
@Test
public void innerInnersFirst() throws IOException {
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
pause(1000);
File f1 = create(dir, "Book$1.class");
watcher.register(f1);
pause(1100);
File f2 = create(dir, "Book.class");
watcher.register(f2);
pause(1100);
File f3 = create(dir, "Book$_2.class");
watcher.register(f3);
pause(1100);
File f4 = create(dir, "Book$Foo.class");
watcher.register(f4);
pause(1100);
File f5 = create(dir, "Book$Foo$1.class");
watcher.register(f5);
pause(1100);
watcher.setPaused(true);
// Whilst paused, touch both files
touch(f5);
touch(f4);
touch(f3);
touch(f2);
touch(f1);
watcher.setPaused(false);
pause(1100);
watcher.shutdown();
// Check that inners reported first
System.out.println(listener.changesDetected);
assertEquals("Book$1.class", listener.changesDetected.get(0));
assertEquals("Book$Foo$1.class", listener.changesDetected.get(1));
assertEquals("Book$Foo.class", listener.changesDetected.get(2));
assertEquals("Book$_2.class", listener.changesDetected.get(3));
assertEquals("Book.class", listener.changesDetected.get(4));
}
private void touch(File f) {
try {
FileOutputStream fos = new FileOutputStream(f);
fos.write(3);
fos.close();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
private File create(File dir, String filename) throws IOException {
File f = new File(dir, filename);
boolean b = f.createNewFile();
Assert.assertTrue(b);
return f;
}
private void pause(long millis) {
try {
Thread.sleep(millis);
}
catch (Exception e) {
}
}
private File getTempDir() {
try {
File tempFile;
tempFile = File.createTempFile("eternal", "");
// File base =
tempFile.getParentFile();
// String name =
// tempFile.getName();
tempFile.delete();
boolean b = tempFile.mkdir();
if (!b) {
throw new RuntimeException("Failed to create folder " + tempFile);
}
return tempFile;
}
catch (IOException e) {
return null;
}
}
static class TestFileChangeListener implements FileChangeListener {
List<String> changesDetected = new ArrayList<String>();
public void fileChanged(File file) {
System.out.println("File change detected " + file);
changesDetected.add(file.getName());
}
public void register(ReloadableType rtype, File file) {
}
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.springsource.loaded.FileChangeListener;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.agent.FileSystemWatcher;
public class FileSystemWatcherTests {
/**
* Create a folder, watch it then put a couple of files in and check they are detected
*/
@Ignore
@Test
public void dirs() throws IOException {
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
watcher.register(dir);
pause(1000);
create(dir, "abc.txt");
pause(1100);
create(dir, "abcd.txt");
pause(1100);
watcher.shutdown();
Assert.assertTrue(listener.changesDetected.contains("abc.txt"));
Assert.assertTrue(listener.changesDetected.contains("abcd.txt"));
}
@Test
public void files() throws IOException {
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
pause(1000);
File f1 = create(dir, "abc.txt");
watcher.register(f1);
pause(1100);
File f2 = create(dir, "abcd.txt");
watcher.register(f2);
pause(1100);
watcher.setPaused(true);
// Whilst paused, touch both files
touch(f2);
touch(f1);
watcher.setPaused(false);
pause(3000);
watcher.shutdown();
System.out.println(listener.changesDetected);
assertEquals("abc.txt", listener.changesDetected.get(0));
assertEquals("abcd.txt", listener.changesDetected.get(1));
}
@Ignore
@Test
public void innersFirst() throws IOException {
System.out.println("innersFirst");
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
pause(1000);
File f1 = create(dir, "Book$1.class");
watcher.register(f1);
pause(1100);
File f2 = create(dir, "Book.class");
watcher.register(f2);
pause(1100);
File f3 = create(dir, "Book$_2.class");
watcher.register(f3);
pause(1100);
watcher.setPaused(true);
// Whilst paused, touch both files
touch(f3);
touch(f2);
touch(f1);
watcher.setPaused(false);
pause(3000);
System.out.println(listener.changesDetected);
watcher.shutdown();
// Check that inners reported first
assertEquals("Book$1.class", listener.changesDetected.get(0));
assertEquals("Book$_2.class", listener.changesDetected.get(1));
assertEquals("Book.class", listener.changesDetected.get(2));
}
@Ignore
@Test
public void innerInnersFirst() throws IOException {
TestFileChangeListener listener = new TestFileChangeListener();
File dir = getTempDir();
FileSystemWatcher watcher = new FileSystemWatcher(listener, -1, "test");
pause(1000);
File f1 = create(dir, "Book$1.class");
watcher.register(f1);
pause(1100);
File f2 = create(dir, "Book.class");
watcher.register(f2);
pause(1100);
File f3 = create(dir, "Book$_2.class");
watcher.register(f3);
pause(1100);
File f4 = create(dir, "Book$Foo.class");
watcher.register(f4);
pause(1100);
File f5 = create(dir, "Book$Foo$1.class");
watcher.register(f5);
pause(1100);
watcher.setPaused(true);
// Whilst paused, touch both files
touch(f5);
touch(f4);
touch(f3);
touch(f2);
touch(f1);
watcher.setPaused(false);
pause(1100);
watcher.shutdown();
// Check that inners reported first
System.out.println(listener.changesDetected);
assertEquals("Book$1.class", listener.changesDetected.get(0));
assertEquals("Book$Foo$1.class", listener.changesDetected.get(1));
assertEquals("Book$Foo.class", listener.changesDetected.get(2));
assertEquals("Book$_2.class", listener.changesDetected.get(3));
assertEquals("Book.class", listener.changesDetected.get(4));
}
private void touch(File f) {
try {
FileOutputStream fos = new FileOutputStream(f);
fos.write(3);
fos.close();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
private File create(File dir, String filename) throws IOException {
File f = new File(dir, filename);
boolean b = f.createNewFile();
Assert.assertTrue(b);
return f;
}
private void pause(long millis) {
try {
Thread.sleep(millis);
}
catch (Exception e) {
}
}
private File getTempDir() {
try {
File tempFile;
tempFile = File.createTempFile("eternal", "");
// File base =
tempFile.getParentFile();
// String name =
// tempFile.getName();
tempFile.delete();
boolean b = tempFile.mkdir();
if (!b) {
throw new RuntimeException("Failed to create folder " + tempFile);
}
return tempFile;
}
catch (IOException e) {
return null;
}
}
static class TestFileChangeListener implements FileChangeListener {
List<String> changesDetected = new ArrayList<String>();
public void fileChanged(File file) {
System.out.println("File change detected " + file);
changesDetected.add(file.getName());
}
public void register(ReloadableType rtype, File file) {
}
}
}

View File

@@ -1,125 +1,125 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Test;
import org.springsource.loaded.InterfaceExtractor;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypeDescriptorExtractor;
import org.springsource.loaded.TypeRegistry;
/**
* Tests for interface extraction.
*
* @author Andy Clement
* @since 1.0
*/
public class InterfaceExtractorTest extends SpringLoadedTests {
/**
* Attempt simple interface extraction for a class with one void no-arg method
*/
@Test
public void simpleExtraction() {
TypeRegistry registry = getTypeRegistry(null);
byte[] classBytes = loadBytesForClass("data.SimpleClass");
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
// @formatter:off
checkType(classBytes,
"CLASS: data/SimpleClass v50 0x0020(synchronized) super java/lang/Object\n" +
"SOURCE: SimpleClass.java null\n" +
"METHOD: 0x0000() <init>()V\n" +
" CODE\n" +
" L0\n" +
" ALOAD 0\n" +
" INVOKESPECIAL java/lang/Object.<init>()V\n" +
" RETURN\n" +
" L1\n" +
"METHOD: 0x0001(public) foo()V\n" +
" CODE\n" +
" L0\n" +
" RETURN\n" +
" L1\n" +
"\n");
// @formatter:on
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
// @formatter:off
checkType(
bytes,
"CLASS: data/SimpleClass__I v50 0x0601(public abstract interface) super java/lang/Object\n"
+
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClass;)V\n"
+
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClass;)V\n"
+
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
+
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClass;)I\n"
+
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClass;Ljava/lang/Object;)Z\n"
+
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClass;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
+
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClass;)Ljava/lang/String;\n" +
"\n");
// @formatter:on
}
@Test
public void varietyOfMethods() {
TypeRegistry registry = getTypeRegistry(null);
byte[] classBytes = loadBytesForClass("data.SimpleClassFour");
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
// @formatter:off
checkType(
bytes,
"CLASS: data/SimpleClassFour__I v50 0x0601(public abstract interface) super java/lang/Object\n"
+
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;I)V\n"
+
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;Ljava/lang/String;)V\n"
+
"METHOD: 0x0401(public abstract) boo(Ldata/SimpleClassFour;)V\n"
+
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClassFour;)V\n"
+
"METHOD: 0x0401(public abstract) goo(Ldata/SimpleClassFour;IDLjava/lang/String;)Ljava/lang/String;\n"
+
"METHOD: 0x0401(public abstract) hoo(Ldata/SimpleClassFour;J)I\n"
+
"METHOD: 0x0401(public abstract) woo(Ldata/SimpleClassFour;)V java/lang/RuntimeException java/lang/IllegalStateException\n"
+
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
+
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClassFour;)I\n"
+
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClassFour;Ljava/lang/Object;)Z\n"
+
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClassFour;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
+
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClassFour;)Ljava/lang/String;\n" +
"\n");
// @formatter:on
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import org.junit.Test;
import org.springsource.loaded.InterfaceExtractor;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypeDescriptorExtractor;
import org.springsource.loaded.TypeRegistry;
/**
* Tests for interface extraction.
*
* @author Andy Clement
* @since 1.0
*/
public class InterfaceExtractorTest extends SpringLoadedTests {
/**
* Attempt simple interface extraction for a class with one void no-arg method
*/
@Test
public void simpleExtraction() {
TypeRegistry registry = getTypeRegistry(null);
byte[] classBytes = loadBytesForClass("data.SimpleClass");
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
// @formatter:off
checkType(classBytes,
"CLASS: data/SimpleClass v50 0x0020(synchronized) super java/lang/Object\n" +
"SOURCE: SimpleClass.java null\n" +
"METHOD: 0x0000() <init>()V\n" +
" CODE\n" +
" L0\n" +
" ALOAD 0\n" +
" INVOKESPECIAL java/lang/Object.<init>()V\n" +
" RETURN\n" +
" L1\n" +
"METHOD: 0x0001(public) foo()V\n" +
" CODE\n" +
" L0\n" +
" RETURN\n" +
" L1\n" +
"\n");
// @formatter:on
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
// @formatter:off
checkType(
bytes,
"CLASS: data/SimpleClass__I v50 0x0601(public abstract interface) super java/lang/Object\n"
+
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClass;)V\n"
+
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClass;)V\n"
+
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
+
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClass;)I\n"
+
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClass;Ljava/lang/Object;)Z\n"
+
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClass;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
+
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClass;)Ljava/lang/String;\n" +
"\n");
// @formatter:on
}
@Test
public void varietyOfMethods() {
TypeRegistry registry = getTypeRegistry(null);
byte[] classBytes = loadBytesForClass("data.SimpleClassFour");
TypeDescriptor td = new TypeDescriptorExtractor(registry).extract(classBytes, true);
byte[] bytes = InterfaceExtractor.extract(classBytes, registry, td);
// @formatter:off
checkType(
bytes,
"CLASS: data/SimpleClassFour__I v50 0x0601(public abstract interface) super java/lang/Object\n"
+
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;I)V\n"
+
"METHOD: 0x0401(public abstract) ___init___(Ldata/SimpleClassFour;Ljava/lang/String;)V\n"
+
"METHOD: 0x0401(public abstract) boo(Ldata/SimpleClassFour;)V\n"
+
"METHOD: 0x0401(public abstract) foo(Ldata/SimpleClassFour;)V\n"
+
"METHOD: 0x0401(public abstract) goo(Ldata/SimpleClassFour;IDLjava/lang/String;)Ljava/lang/String;\n"
+
"METHOD: 0x0401(public abstract) hoo(Ldata/SimpleClassFour;J)I\n"
+
"METHOD: 0x0401(public abstract) woo(Ldata/SimpleClassFour;)V java/lang/RuntimeException java/lang/IllegalStateException\n"
+
"METHOD: 0x0401(public abstract) __execute([Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;\n"
+
"METHOD: 0x0401(public abstract) ___clinit___()V\n"
+
"METHOD: 0x0401(public abstract) hashCode(Ldata/SimpleClassFour;)I\n"
+
"METHOD: 0x0401(public abstract) equals(Ldata/SimpleClassFour;Ljava/lang/Object;)Z\n"
+
"METHOD: 0x0401(public abstract) clone(Ldata/SimpleClassFour;)Ljava/lang/Object; java/lang/CloneNotSupportedException\n"
+
"METHOD: 0x0401(public abstract) toString(Ldata/SimpleClassFour;)Ljava/lang/String;\n" +
"\n");
// @formatter:on
}
}

View File

@@ -1,49 +1,49 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.springsource.loaded.NameRegistry;
/**
* Test the NameRegistry behaves as expected.
*
* @author Andy Clement
* @since 0.8.1
*/
public class NameRegistryTests extends SpringLoadedTests {
@Test
public void byTheNumbers() {
assertEquals(-1, NameRegistry.getIdFor("a/b/C"));
assertEquals(0, NameRegistry.getIdOrAllocateFor("a/b/C"));
assertEquals(0, NameRegistry.getIdFor("a/b/C"));
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
// test expansion
for (int i = 0; i < 100; i++) {
assertEquals(2 + i, NameRegistry.getIdOrAllocateFor("a/b/C" + i));
}
assertEquals("a/b/C", NameRegistry.getTypenameById(0));
assertNull(NameRegistry.getTypenameById(1000));
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.Test;
import org.springsource.loaded.NameRegistry;
/**
* Test the NameRegistry behaves as expected.
*
* @author Andy Clement
* @since 0.8.1
*/
public class NameRegistryTests extends SpringLoadedTests {
@Test
public void byTheNumbers() {
assertEquals(-1, NameRegistry.getIdFor("a/b/C"));
assertEquals(0, NameRegistry.getIdOrAllocateFor("a/b/C"));
assertEquals(0, NameRegistry.getIdFor("a/b/C"));
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
assertEquals(1, NameRegistry.getIdOrAllocateFor("d/e/F"));
// test expansion
for (int i = 0; i < 100; i++) {
assertEquals(2 + i, NameRegistry.getIdOrAllocateFor("a/b/C" + i));
}
assertEquals("a/b/C", NameRegistry.getTypenameById(0));
assertNull(NameRegistry.getTypenameById(1000));
}
}

View File

@@ -1,173 +1,173 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.lang.reflect.Modifier;
import org.junit.Test;
import org.springsource.loaded.FieldMember;
import org.springsource.loaded.MethodMember;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypeDescriptorExtractor;
import org.springsource.loaded.TypeRegistry;
/**
* Tests for TypeDescriptor usage.
*
* @author Andy Clement
* @since 1.0
*/
public class TypeDescriptorTests extends SpringLoadedTests {
@Test
public void simpleMethodDescriptors() {
TypeRegistry registry = getTypeRegistry("data.SimpleClass");
byte[] bytes = loadBytesForClass("data.SimpleClass");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
assertEquals("data/SimpleClass", typeDescriptor.getName());
assertEquals("java/lang/Object", typeDescriptor.getSupertypeName());
assertEquals(0, typeDescriptor.getSuperinterfacesName().length);
assertEquals(0x20, typeDescriptor.getModifiers());
assertEquals(0, typeDescriptor.getFields().length);
assertEquals(5, typeDescriptor.getMethods().length); // will include catchers
assertEquals("0x1 foo()V", typeDescriptor.getMethods()[0].toString());
}
@Test
public void complexMethodDescriptors() {
TypeRegistry registry = getTypeRegistry("data.ComplexClass");
byte[] bytes = loadBytesForClass("data.ComplexClass");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
assertEquals("data/ComplexClass", typeDescriptor.getName());
assertEquals("data/SimpleClass", typeDescriptor.getSupertypeName());
assertEquals(1, typeDescriptor.getSuperinterfacesName().length);
assertEquals("java/io/Serializable", typeDescriptor.getSuperinterfacesName()[0]);
assertEquals(0x20, typeDescriptor.getModifiers());
assertEquals(3, typeDescriptor.getFields().length);
assertEquals(9, typeDescriptor.getMethods().length);
assertEquals("0x2 privateMethod()I", typeDescriptor.getMethods()[0].toString());
assertEquals("0x1 publicMethod()Ljava/lang/String;", typeDescriptor.getMethods()[1].toString());
assertEquals("0x0 defaultMethod()Ljava/util/List;", typeDescriptor.getMethods()[2].toString());
assertEquals("0x0 thrower()V throws java/lang/Exception java/lang/IllegalStateException",
typeDescriptor.getMethods()[3].toString());
}
@Test
public void fieldDescriptors() {
TypeRegistry registry = getTypeRegistry("");
byte[] bytes = loadBytesForClass("data.SomeFields");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
FieldMember[] fields = typeDescriptor.getFields();
assertEquals(4, fields.length);
FieldMember privateField = fields[0];
assertEquals(Modifier.PRIVATE, privateField.getModifiers());
assertEquals("privateField", privateField.getName());
assertEquals("I", privateField.getDescriptor());
assertNull(privateField.getGenericSignature());
assertEquals("0x2 I privateField", privateField.toString());
FieldMember publicField = fields[1];
assertEquals(Modifier.PUBLIC, publicField.getModifiers());
assertEquals("publicField", publicField.getName());
assertEquals("Ljava/lang/String;", publicField.getDescriptor());
assertNull(publicField.getGenericSignature());
assertEquals("0x1 Ljava/lang/String; publicField", publicField.toString());
FieldMember defaultField = fields[2];
assertEquals(0, defaultField.getModifiers());
assertEquals("defaultField", defaultField.getName());
assertEquals("Ljava/util/List;", defaultField.getDescriptor());
assertEquals("Ljava/util/List<Ljava/lang/String;>;", defaultField.getGenericSignature());
assertEquals("0x0 Ljava/util/List; defaultField [Ljava/util/List<Ljava/lang/String;>;]",
defaultField.toString());
FieldMember protectedField = fields[3];
assertEquals(Modifier.PROTECTED, protectedField.getModifiers());
assertEquals("protectedField", protectedField.getName());
assertEquals("Ljava/util/Map;", protectedField.getDescriptor());
assertEquals("Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;",
protectedField.getGenericSignature());
assertEquals(
"0x4 Ljava/util/Map; protectedField [Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;]",
protectedField.toString());
}
@Test
public void constructorDescriptors() {
TypeRegistry registry = getTypeRegistry("");
byte[] bytes = loadBytesForClass("data.SomeConstructors");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
MethodMember[] ctors = typeDescriptor.getConstructors();
assertEquals(3, ctors.length);
MethodMember publicCtor = ctors[0];
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
assertEquals("<init>", publicCtor.getName());
assertEquals("()V", publicCtor.getDescriptor());
assertNull(publicCtor.getGenericSignature());
assertEquals("0x1 <init>()V", publicCtor.toString());
MethodMember privateCtor = ctors[1];
assertEquals(Modifier.PRIVATE, privateCtor.getModifiers());
assertEquals("<init>", privateCtor.getName());
assertEquals("(Ljava/lang/String;I)V", privateCtor.getDescriptor());
assertNull(privateCtor.getGenericSignature());
assertEquals("0x2 <init>(Ljava/lang/String;I)V", privateCtor.toString());
MethodMember protCtor = ctors[2];
assertEquals(Modifier.PROTECTED, protCtor.getModifiers());
assertEquals("<init>", protCtor.getName());
assertEquals("(J)V", protCtor.getDescriptor());
assertNull(protCtor.getGenericSignature());
assertEquals("0x4 <init>(J)V", protCtor.toString());
}
@Test
public void constructorDescriptorsAfterReloading() {
TypeRegistry registry = getTypeRegistry("");
String d = "data.SomeConstructors";
ReloadableType rtype = registry.addType(d, loadBytesForClass(d));
MethodMember[] latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
assertEquals(3, latestConstructors.length);
rtype.loadNewVersion("2", retrieveRename(d, d + "002"));
latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
assertEquals(1, latestConstructors.length);
}
@Test
public void defaultConstructorDescriptor() {
TypeRegistry registry = getTypeRegistry("");
byte[] bytes = loadBytesForClass("data.SomeConstructors2");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
MethodMember[] ctors = typeDescriptor.getConstructors();
assertEquals(1, ctors.length);
MethodMember publicCtor = ctors[0];
// visibility matches type vis (for public/default)
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
assertEquals("<init>", publicCtor.getName());
assertEquals("()V", publicCtor.getDescriptor());
assertNull(publicCtor.getGenericSignature());
assertEquals("0x1 <init>()V", publicCtor.toString());
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import java.lang.reflect.Modifier;
import org.junit.Test;
import org.springsource.loaded.FieldMember;
import org.springsource.loaded.MethodMember;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypeDescriptorExtractor;
import org.springsource.loaded.TypeRegistry;
/**
* Tests for TypeDescriptor usage.
*
* @author Andy Clement
* @since 1.0
*/
public class TypeDescriptorTests extends SpringLoadedTests {
@Test
public void simpleMethodDescriptors() {
TypeRegistry registry = getTypeRegistry("data.SimpleClass");
byte[] bytes = loadBytesForClass("data.SimpleClass");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
assertEquals("data/SimpleClass", typeDescriptor.getName());
assertEquals("java/lang/Object", typeDescriptor.getSupertypeName());
assertEquals(0, typeDescriptor.getSuperinterfacesName().length);
assertEquals(0x20, typeDescriptor.getModifiers());
assertEquals(0, typeDescriptor.getFields().length);
assertEquals(5, typeDescriptor.getMethods().length); // will include catchers
assertEquals("0x1 foo()V", typeDescriptor.getMethods()[0].toString());
}
@Test
public void complexMethodDescriptors() {
TypeRegistry registry = getTypeRegistry("data.ComplexClass");
byte[] bytes = loadBytesForClass("data.ComplexClass");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, true);
assertEquals("data/ComplexClass", typeDescriptor.getName());
assertEquals("data/SimpleClass", typeDescriptor.getSupertypeName());
assertEquals(1, typeDescriptor.getSuperinterfacesName().length);
assertEquals("java/io/Serializable", typeDescriptor.getSuperinterfacesName()[0]);
assertEquals(0x20, typeDescriptor.getModifiers());
assertEquals(3, typeDescriptor.getFields().length);
assertEquals(9, typeDescriptor.getMethods().length);
assertEquals("0x2 privateMethod()I", typeDescriptor.getMethods()[0].toString());
assertEquals("0x1 publicMethod()Ljava/lang/String;", typeDescriptor.getMethods()[1].toString());
assertEquals("0x0 defaultMethod()Ljava/util/List;", typeDescriptor.getMethods()[2].toString());
assertEquals("0x0 thrower()V throws java/lang/Exception java/lang/IllegalStateException",
typeDescriptor.getMethods()[3].toString());
}
@Test
public void fieldDescriptors() {
TypeRegistry registry = getTypeRegistry("");
byte[] bytes = loadBytesForClass("data.SomeFields");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
FieldMember[] fields = typeDescriptor.getFields();
assertEquals(4, fields.length);
FieldMember privateField = fields[0];
assertEquals(Modifier.PRIVATE, privateField.getModifiers());
assertEquals("privateField", privateField.getName());
assertEquals("I", privateField.getDescriptor());
assertNull(privateField.getGenericSignature());
assertEquals("0x2 I privateField", privateField.toString());
FieldMember publicField = fields[1];
assertEquals(Modifier.PUBLIC, publicField.getModifiers());
assertEquals("publicField", publicField.getName());
assertEquals("Ljava/lang/String;", publicField.getDescriptor());
assertNull(publicField.getGenericSignature());
assertEquals("0x1 Ljava/lang/String; publicField", publicField.toString());
FieldMember defaultField = fields[2];
assertEquals(0, defaultField.getModifiers());
assertEquals("defaultField", defaultField.getName());
assertEquals("Ljava/util/List;", defaultField.getDescriptor());
assertEquals("Ljava/util/List<Ljava/lang/String;>;", defaultField.getGenericSignature());
assertEquals("0x0 Ljava/util/List; defaultField [Ljava/util/List<Ljava/lang/String;>;]",
defaultField.toString());
FieldMember protectedField = fields[3];
assertEquals(Modifier.PROTECTED, protectedField.getModifiers());
assertEquals("protectedField", protectedField.getName());
assertEquals("Ljava/util/Map;", protectedField.getDescriptor());
assertEquals("Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;",
protectedField.getGenericSignature());
assertEquals(
"0x4 Ljava/util/Map; protectedField [Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/Integer;>;>;]",
protectedField.toString());
}
@Test
public void constructorDescriptors() {
TypeRegistry registry = getTypeRegistry("");
byte[] bytes = loadBytesForClass("data.SomeConstructors");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
MethodMember[] ctors = typeDescriptor.getConstructors();
assertEquals(3, ctors.length);
MethodMember publicCtor = ctors[0];
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
assertEquals("<init>", publicCtor.getName());
assertEquals("()V", publicCtor.getDescriptor());
assertNull(publicCtor.getGenericSignature());
assertEquals("0x1 <init>()V", publicCtor.toString());
MethodMember privateCtor = ctors[1];
assertEquals(Modifier.PRIVATE, privateCtor.getModifiers());
assertEquals("<init>", privateCtor.getName());
assertEquals("(Ljava/lang/String;I)V", privateCtor.getDescriptor());
assertNull(privateCtor.getGenericSignature());
assertEquals("0x2 <init>(Ljava/lang/String;I)V", privateCtor.toString());
MethodMember protCtor = ctors[2];
assertEquals(Modifier.PROTECTED, protCtor.getModifiers());
assertEquals("<init>", protCtor.getName());
assertEquals("(J)V", protCtor.getDescriptor());
assertNull(protCtor.getGenericSignature());
assertEquals("0x4 <init>(J)V", protCtor.toString());
}
@Test
public void constructorDescriptorsAfterReloading() {
TypeRegistry registry = getTypeRegistry("");
String d = "data.SomeConstructors";
ReloadableType rtype = registry.addType(d, loadBytesForClass(d));
MethodMember[] latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
assertEquals(3, latestConstructors.length);
rtype.loadNewVersion("2", retrieveRename(d, d + "002"));
latestConstructors = rtype.getLatestTypeDescriptor().getConstructors();
assertEquals(1, latestConstructors.length);
}
@Test
public void defaultConstructorDescriptor() {
TypeRegistry registry = getTypeRegistry("");
byte[] bytes = loadBytesForClass("data.SomeConstructors2");
TypeDescriptor typeDescriptor = new TypeDescriptorExtractor(registry).extract(bytes, false);
MethodMember[] ctors = typeDescriptor.getConstructors();
assertEquals(1, ctors.length);
MethodMember publicCtor = ctors[0];
// visibility matches type vis (for public/default)
assertEquals(Modifier.PUBLIC, publicCtor.getModifiers());
assertEquals("<init>", publicCtor.getName());
assertEquals("()V", publicCtor.getDescriptor());
assertNull(publicCtor.getGenericSignature());
assertEquals("0x1 <init>()V", publicCtor.toString());
}
}

View File

@@ -1,295 +1,295 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.junit.Test;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypePattern;
import org.springsource.loaded.TypeRegistry;
/**
* Tests for the TypeRegistry that exercise it in the same way it will actively be used when managing ReloadableType
* instances.
*
* @author Andy Clement
* @since 1.0
*/
public class TypeRegistryTests extends SpringLoadedTests {
@Test
public void basics() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
typeRegistry = TypeRegistry.getTypeRegistryFor(null);
assertNull(typeRegistry);
}
/**
* Same instance for two different calls passing the same classloader.
*/
@Test
public void sameInstance() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
TypeRegistry typeRegistry2 = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry2);
assertTrue(typeRegistry == typeRegistry2);
}
@Test
public void loadingDescriptors() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
TypeDescriptor jloDescriptor = typeRegistry.getDescriptorFor("java/lang/Object");
assertNotNull(jloDescriptor);
assertEquals("java/lang/Object", jloDescriptor.getName());
}
@Test
public void descriptorsWithCatchers() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
TypeDescriptor dscDescriptor = typeRegistry.getDescriptorFor("data/SimpleClass");
assertNotNull(dscDescriptor);
assertEquals("data/SimpleClass", dscDescriptor.getName());
// check for a catcher
assertNotNull(findMethod("0x1 toString()Ljava/lang/String;", dscDescriptor));
}
@Test
public void descriptorsWithCatchers2() {
// more complicated hierarchy
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
TypeDescriptor topDescriptor = typeRegistry.getDescriptorFor("catchers/Top");
assertNotNull(topDescriptor);
// Checking no toString() catcher because Top defines a toString()
assertEquals(5, topDescriptor.getMethods().length);
// if 'Top' is not considered reloadable we will get an entry for 'foo' that is inherited from it
TypeDescriptor middleDescriptor = typeRegistry.getDescriptorFor("catchers/Middle");
assertNotNull(middleDescriptor);
assertEquals(5, middleDescriptor.getMethods().length);
TypeDescriptor bottomDescriptor = typeRegistry.getDescriptorFor("catchers/Bottom");
assertNotNull(bottomDescriptor);
System.out.println(bottomDescriptor.toString());
assertEquals(5, bottomDescriptor.getMethods().length);
}
@Test
public void includesExcludes() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
typeRegistry.configure(p);
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertEquals("text:com.foo.Bar", tps.get(0).toString());
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
typeRegistry.configure(p);
tps = typeRegistry.getInclusionPatterns();
System.out.println(tps);
assertEquals(2, tps.size());
assertEquals("text:com.foo.Bar", tps.get(0).toString());
assertEquals("text:org.springsource..*", tps.get(1).toString());
assertTrue(typeRegistry.isReloadableTypeName("com/foo/Bar"));
assertFalse(typeRegistry.isReloadableTypeName("com/foo/Garr"));
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/Garr"));
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/sub/Garr"));
assertFalse(typeRegistry.isReloadableTypeName("Boo"));
}
@Test
public void includesExcludes2() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
typeRegistry.configure(p);
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertEquals("text:com.foo.Bar", tps.get(0).toString());
assertFalse(tps.get(0).matches("com.foo.Gar"));
assertTrue(tps.get(0).matches("com.foo.Bar"));
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
typeRegistry.configure(p);
tps = typeRegistry.getInclusionPatterns();
assertEquals(2, tps.size());
// exclude should be first
assertEquals("text:com.foo.Bar", tps.get(0).toString());
assertEquals("text:org.springsource..*", tps.get(1).toString());
assertFalse(tps.get(0).matches("com.foo.Gar"));
assertTrue(tps.get(0).matches("com.foo.Bar"));
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo..*");
typeRegistry.configure(p);
tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertFalse(tps.get(0).matches("com.goo.Bar"));
assertTrue(tps.get(0).matches("com.foo.Bar"));
}
@Test
public void includesExcludes3() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_Inclusions, "*");
typeRegistry.configure(p);
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertEquals("text:*", tps.get(0).toString());
assertTrue(tps.get(0).matches("wibble"));
p.setProperty(TypeRegistry.Key_Exclusions, "*");
typeRegistry.configure(p);
tps = typeRegistry.getExclusionPatterns();
assertEquals("text:*", tps.get(0).toString());
assertTrue(tps.get(0).matches("wibble"));
}
@Test
public void loadTypeBadNames() {
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass002");
assertFalse(typeRegistry.isReloadableTypeName("data/SimpleClass"));
assertFalse(typeRegistry.isReloadableTypeName("com/bar"));
}
@Test
public void loadType2() {
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
assertTrue(typeRegistry.isReloadableTypeName("data/SimpleClass"));
byte[] dsc = loadBytesForClass("data.SimpleClass");
ReloadableType rtype = typeRegistry.addType("data.SimpleClass", dsc);
assertNotNull(rtype);
}
@Test
public void rebasePaths() {
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_ReloadableRebase, "a/b/c=d/e/f,g/h=x/y");
typeRegistry.configure(p);
Map<String, String> rebases = typeRegistry.getRebasePaths();
assertEquals(2, rebases.keySet().size());
String value = rebases.get("a/b/c");
assertEquals("d/e/f", value);
assertEquals("x/y", rebases.get("g/h"));
}
/**
* Test that when the child classloader being managed by the type registry has reached the limit, it is recreated
* and types are then defined on the fly as it is used (dispatchers/executors).
*/
@Test
public void classloaderRecreation() throws Exception {
String one = "basic.Basic";
String two = "basic.BasicB";
GlobalConfiguration.maxClassDefinitions = 4;
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two);
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
result = runUnguarded(tOne.getClazz(), "getValue");
assertEquals(5, result.returnValue);
// Should be nothing defined in the child loader
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
// Should be dispatcher and executor for the reloaded type
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
assertEquals(7, runUnguarded(tOne.getClazz(), "getValue").returnValue);
tTwo.loadNewVersion("002", tTwo.bytesInitial);
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
result = runUnguarded(tOne.getClazz(), "getValue");
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
Class<?> cOneExecutor = tOne.getLatestExecutorClass();
tOne.loadNewVersion("003", tOne.bytesInitial);
// Now on this reload the child classloader should be recreated as it already has more
// than 2 defined.
// Note: this will currently cause us to redefine all the reloadable types
// according to their most recent version. An optimization may be to only
// define them on demand
tTwo.loadNewVersion("002", tTwo.bytesInitial);
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
// But what about calling the older types?
assertEquals(5, runUnguarded(tOne.getClazz(), "getValue").returnValue);
if (cOneExecutor == tOne.getLatestExecutorClass()) {
fail("Why are we not using a new executor? the old one should have been removed, freeing up the classloader");
}
}
/**
* Checking that the counting is working correctly for the managed classloader.
*/
@Test
public void classloaderCounting() throws Exception {
String one = "basic.Basic";
String two = "basic.BasicB";
String three = "basic.BasicC";
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two + "," + three);
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
ReloadableType tThree = typeRegistry.addType(three, loadBytesForClass(three));
result = runUnguarded(tOne.getClazz(), "getValue");
assertEquals(5, result.returnValue);
// Should be nothing defined in the child loader
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
// Should be dispatcher and executor for the reloaded type
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
tTwo.loadNewVersion("002", tTwo.bytesInitial);
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
tThree.loadNewVersion("002", tThree.bytesInitial);
assertEquals(6, typeRegistry.getChildClassLoader().getDefinedCount());
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.junit.Test;
import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeDescriptor;
import org.springsource.loaded.TypePattern;
import org.springsource.loaded.TypeRegistry;
/**
* Tests for the TypeRegistry that exercise it in the same way it will actively be used when managing ReloadableType
* instances.
*
* @author Andy Clement
* @since 1.0
*/
public class TypeRegistryTests extends SpringLoadedTests {
@Test
public void basics() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
typeRegistry = TypeRegistry.getTypeRegistryFor(null);
assertNull(typeRegistry);
}
/**
* Same instance for two different calls passing the same classloader.
*/
@Test
public void sameInstance() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
TypeRegistry typeRegistry2 = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry2);
assertTrue(typeRegistry == typeRegistry2);
}
@Test
public void loadingDescriptors() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
TypeDescriptor jloDescriptor = typeRegistry.getDescriptorFor("java/lang/Object");
assertNotNull(jloDescriptor);
assertEquals("java/lang/Object", jloDescriptor.getName());
}
@Test
public void descriptorsWithCatchers() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
TypeDescriptor dscDescriptor = typeRegistry.getDescriptorFor("data/SimpleClass");
assertNotNull(dscDescriptor);
assertEquals("data/SimpleClass", dscDescriptor.getName());
// check for a catcher
assertNotNull(findMethod("0x1 toString()Ljava/lang/String;", dscDescriptor));
}
@Test
public void descriptorsWithCatchers2() {
// more complicated hierarchy
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
TypeDescriptor topDescriptor = typeRegistry.getDescriptorFor("catchers/Top");
assertNotNull(topDescriptor);
// Checking no toString() catcher because Top defines a toString()
assertEquals(5, topDescriptor.getMethods().length);
// if 'Top' is not considered reloadable we will get an entry for 'foo' that is inherited from it
TypeDescriptor middleDescriptor = typeRegistry.getDescriptorFor("catchers/Middle");
assertNotNull(middleDescriptor);
assertEquals(5, middleDescriptor.getMethods().length);
TypeDescriptor bottomDescriptor = typeRegistry.getDescriptorFor("catchers/Bottom");
assertNotNull(bottomDescriptor);
System.out.println(bottomDescriptor.toString());
assertEquals(5, bottomDescriptor.getMethods().length);
}
@Test
public void includesExcludes() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
typeRegistry.configure(p);
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertEquals("text:com.foo.Bar", tps.get(0).toString());
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
typeRegistry.configure(p);
tps = typeRegistry.getInclusionPatterns();
System.out.println(tps);
assertEquals(2, tps.size());
assertEquals("text:com.foo.Bar", tps.get(0).toString());
assertEquals("text:org.springsource..*", tps.get(1).toString());
assertTrue(typeRegistry.isReloadableTypeName("com/foo/Bar"));
assertFalse(typeRegistry.isReloadableTypeName("com/foo/Garr"));
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/Garr"));
assertTrue(typeRegistry.isReloadableTypeName("org/springsource/sub/Garr"));
assertFalse(typeRegistry.isReloadableTypeName("Boo"));
}
@Test
public void includesExcludes2() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar");
typeRegistry.configure(p);
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertEquals("text:com.foo.Bar", tps.get(0).toString());
assertFalse(tps.get(0).matches("com.foo.Gar"));
assertTrue(tps.get(0).matches("com.foo.Bar"));
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo.Bar,org.springsource..*");
typeRegistry.configure(p);
tps = typeRegistry.getInclusionPatterns();
assertEquals(2, tps.size());
// exclude should be first
assertEquals("text:com.foo.Bar", tps.get(0).toString());
assertEquals("text:org.springsource..*", tps.get(1).toString());
assertFalse(tps.get(0).matches("com.foo.Gar"));
assertTrue(tps.get(0).matches("com.foo.Bar"));
p.setProperty(TypeRegistry.Key_Inclusions, "com.foo..*");
typeRegistry.configure(p);
tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertFalse(tps.get(0).matches("com.goo.Bar"));
assertTrue(tps.get(0).matches("com.foo.Bar"));
}
@Test
public void includesExcludes3() {
TypeRegistry typeRegistry = TypeRegistry.getTypeRegistryFor(binLoader);
assertNotNull(typeRegistry);
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_Inclusions, "*");
typeRegistry.configure(p);
List<TypePattern> tps = typeRegistry.getInclusionPatterns();
assertEquals(1, tps.size());
assertEquals("text:*", tps.get(0).toString());
assertTrue(tps.get(0).matches("wibble"));
p.setProperty(TypeRegistry.Key_Exclusions, "*");
typeRegistry.configure(p);
tps = typeRegistry.getExclusionPatterns();
assertEquals("text:*", tps.get(0).toString());
assertTrue(tps.get(0).matches("wibble"));
}
@Test
public void loadTypeBadNames() {
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass002");
assertFalse(typeRegistry.isReloadableTypeName("data/SimpleClass"));
assertFalse(typeRegistry.isReloadableTypeName("com/bar"));
}
@Test
public void loadType2() {
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
assertTrue(typeRegistry.isReloadableTypeName("data/SimpleClass"));
byte[] dsc = loadBytesForClass("data.SimpleClass");
ReloadableType rtype = typeRegistry.addType("data.SimpleClass", dsc);
assertNotNull(rtype);
}
@Test
public void rebasePaths() {
TypeRegistry typeRegistry = getTypeRegistry("data.SimpleClass");
Properties p = new Properties();
p.setProperty(TypeRegistry.Key_ReloadableRebase, "a/b/c=d/e/f,g/h=x/y");
typeRegistry.configure(p);
Map<String, String> rebases = typeRegistry.getRebasePaths();
assertEquals(2, rebases.keySet().size());
String value = rebases.get("a/b/c");
assertEquals("d/e/f", value);
assertEquals("x/y", rebases.get("g/h"));
}
/**
* Test that when the child classloader being managed by the type registry has reached the limit, it is recreated
* and types are then defined on the fly as it is used (dispatchers/executors).
*/
@Test
public void classloaderRecreation() throws Exception {
String one = "basic.Basic";
String two = "basic.BasicB";
GlobalConfiguration.maxClassDefinitions = 4;
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two);
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
result = runUnguarded(tOne.getClazz(), "getValue");
assertEquals(5, result.returnValue);
// Should be nothing defined in the child loader
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
// Should be dispatcher and executor for the reloaded type
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
assertEquals(7, runUnguarded(tOne.getClazz(), "getValue").returnValue);
tTwo.loadNewVersion("002", tTwo.bytesInitial);
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
result = runUnguarded(tOne.getClazz(), "getValue");
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
Class<?> cOneExecutor = tOne.getLatestExecutorClass();
tOne.loadNewVersion("003", tOne.bytesInitial);
// Now on this reload the child classloader should be recreated as it already has more
// than 2 defined.
// Note: this will currently cause us to redefine all the reloadable types
// according to their most recent version. An optimization may be to only
// define them on demand
tTwo.loadNewVersion("002", tTwo.bytesInitial);
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
assertEquals(5, runUnguarded(tTwo.getClazz(), "getValue").returnValue);
// But what about calling the older types?
assertEquals(5, runUnguarded(tOne.getClazz(), "getValue").returnValue);
if (cOneExecutor == tOne.getLatestExecutorClass()) {
fail("Why are we not using a new executor? the old one should have been removed, freeing up the classloader");
}
}
/**
* Checking that the counting is working correctly for the managed classloader.
*/
@Test
public void classloaderCounting() throws Exception {
String one = "basic.Basic";
String two = "basic.BasicB";
String three = "basic.BasicC";
TypeRegistry typeRegistry = getTypeRegistry(one + "," + two + "," + three);
ReloadableType tOne = typeRegistry.addType(one, loadBytesForClass(one));
ReloadableType tTwo = typeRegistry.addType(two, loadBytesForClass(two));
ReloadableType tThree = typeRegistry.addType(three, loadBytesForClass(three));
result = runUnguarded(tOne.getClazz(), "getValue");
assertEquals(5, result.returnValue);
// Should be nothing defined in the child loader
assertEquals(0, typeRegistry.getChildClassLoader().getDefinedCount());
tOne.loadNewVersion("002", retrieveRename(one, one + "002"));
// Should be dispatcher and executor for the reloaded type
assertEquals(2, typeRegistry.getChildClassLoader().getDefinedCount());
tTwo.loadNewVersion("002", tTwo.bytesInitial);
assertEquals(4, typeRegistry.getChildClassLoader().getDefinedCount());
tThree.loadNewVersion("002", tThree.bytesInitial);
assertEquals(6, typeRegistry.getChildClassLoader().getDefinedCount());
}
}

View File

@@ -1,128 +1,128 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
import org.springsource.loaded.Utils;
/**
* MethodVisitor that records events - very useful for testing
*/
public class FakeMethodVisitor extends MethodVisitor implements Constants {
public FakeMethodVisitor() {
super(ASM5);
}
StringBuilder events = new StringBuilder();
public String getEvents() {
return events.toString().trim();
}
public void clearEvents() {
events = new StringBuilder();
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public AnnotationVisitor visitAnnotationDefault() {
return null;
}
public void visitAttribute(Attribute attr) {
}
public void visitCode() {
}
public void visitEnd() {
}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
}
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
}
public void visitIincInsn(int var, int increment) {
}
public void visitInsn(int opcode) {
events.append("visitInsn(" + Utils.toOpcodeString(opcode) + ") ");
}
public void visitIntInsn(int opcode, int operand) {
}
public void visitJumpInsn(int opcode, Label label) {
}
public void visitLabel(Label label) {
}
public void visitLdcInsn(Object cst) {
events.append("visitLdcInsn(" + cst + ") ");
}
public void visitLineNumber(int line, Label start) {
}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
}
public void visitMaxs(int maxStack, int maxLocals) {
}
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
events.append("visitMethodInsn(" + Utils.toOpcodeString(opcode) + "," + owner + "," + name + "," + desc + ") ");
}
public void visitMultiANewArrayInsn(String desc, int dims) {
}
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
}
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
}
public void visitTypeInsn(int opcode, String type) {
events.append("visitTypeInsn(" + Utils.toOpcodeString(opcode) + "," + type + ") ");
}
public void visitVarInsn(int opcode, int var) {
events.append("visitVarInsn(" + Utils.toOpcodeString(opcode) + "," + var + ") ");
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
import org.springsource.loaded.Utils;
/**
* MethodVisitor that records events - very useful for testing
*/
public class FakeMethodVisitor extends MethodVisitor implements Constants {
public FakeMethodVisitor() {
super(ASM5);
}
StringBuilder events = new StringBuilder();
public String getEvents() {
return events.toString().trim();
}
public void clearEvents() {
events = new StringBuilder();
}
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
return null;
}
public AnnotationVisitor visitAnnotationDefault() {
return null;
}
public void visitAttribute(Attribute attr) {
}
public void visitCode() {
}
public void visitEnd() {
}
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
}
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
}
public void visitIincInsn(int var, int increment) {
}
public void visitInsn(int opcode) {
events.append("visitInsn(" + Utils.toOpcodeString(opcode) + ") ");
}
public void visitIntInsn(int opcode, int operand) {
}
public void visitJumpInsn(int opcode, Label label) {
}
public void visitLabel(Label label) {
}
public void visitLdcInsn(Object cst) {
events.append("visitLdcInsn(" + cst + ") ");
}
public void visitLineNumber(int line, Label start) {
}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
}
public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
}
public void visitMaxs(int maxStack, int maxLocals) {
}
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
events.append("visitMethodInsn(" + Utils.toOpcodeString(opcode) + "," + owner + "," + name + "," + desc + ") ");
}
public void visitMultiANewArrayInsn(String desc, int dims) {
}
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
return null;
}
public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
}
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
}
public void visitTypeInsn(int opcode, String type) {
events.append("visitTypeInsn(" + Utils.toOpcodeString(opcode) + "," + type + ") ");
}
public void visitVarInsn(int opcode, int var) {
events.append("visitVarInsn(" + Utils.toOpcodeString(opcode) + "," + var + ") ");
}
}

View File

@@ -1,99 +1,99 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
/**
* Captures the result of executing a method: the return value from the method, the stdout and the stderr.
*
* @author Andy Clement
* @since 1.0
*/
public class Result implements IResult {
public Object returnValue;
public final String stdout;
public final String stderr;
public Result(Object returnValue, String stdout, String stderr) {
this.returnValue = returnValue;
if (stdout.endsWith("\n")) {
stdout = stdout.substring(0, stdout.length() - 1);
}
if (stderr.endsWith("\n")) {
stderr = stderr.substring(0, stderr.length() - 1);
}
this.stdout = stdout.trim();
this.stderr = stderr.trim();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("============================\n");
sb.append("ReturnValue was '" + returnValue + "'\n");
sb.append("STDOUT was\n" + stdout + "\n");
sb.append("STDERR was\n" + stderr + "\n");
sb.append("============================\n");
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((returnValue == null) ? 0 : returnValue.hashCode());
result = prime * result + ((stderr == null) ? 0 : stderr.hashCode());
result = prime * result + ((stdout == null) ? 0 : stdout.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Result other = (Result) obj;
if (returnValue == null) {
if (other.returnValue != null)
return false;
}
else if (!returnValue.equals(other.returnValue))
return false;
if (stderr == null) {
if (other.stderr != null)
return false;
}
else if (!stderr.equals(other.stderr))
return false;
if (stdout == null) {
if (other.stdout != null)
return false;
}
else if (!stdout.equals(other.stdout))
return false;
return true;
}
public String getSummary() {
return "" + returnValue;
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
/**
* Captures the result of executing a method: the return value from the method, the stdout and the stderr.
*
* @author Andy Clement
* @since 1.0
*/
public class Result implements IResult {
public Object returnValue;
public final String stdout;
public final String stderr;
public Result(Object returnValue, String stdout, String stderr) {
this.returnValue = returnValue;
if (stdout.endsWith("\n")) {
stdout = stdout.substring(0, stdout.length() - 1);
}
if (stderr.endsWith("\n")) {
stderr = stderr.substring(0, stderr.length() - 1);
}
this.stdout = stdout.trim();
this.stderr = stderr.trim();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("============================\n");
sb.append("ReturnValue was '" + returnValue + "'\n");
sb.append("STDOUT was\n" + stdout + "\n");
sb.append("STDERR was\n" + stderr + "\n");
sb.append("============================\n");
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((returnValue == null) ? 0 : returnValue.hashCode());
result = prime * result + ((stderr == null) ? 0 : stderr.hashCode());
result = prime * result + ((stdout == null) ? 0 : stdout.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Result other = (Result) obj;
if (returnValue == null) {
if (other.returnValue != null)
return false;
}
else if (!returnValue.equals(other.returnValue))
return false;
if (stderr == null) {
if (other.stderr != null)
return false;
}
else if (!stderr.equals(other.stderr))
return false;
if (stdout == null) {
if (other.stdout != null)
return false;
}
else if (!stdout.equals(other.stdout))
return false;
return true;
}
public String getSummary() {
return "" + returnValue;
}
}

View File

@@ -1,139 +1,139 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springsource.loaded.testgen.GenerativeSpringLoadedTest;
/**
* Captures the exception raised by executing a method: the exception the stdout and the stderr.
*
* @author Kris De Volder
* @since 1.0
*/
@SuppressWarnings("serial")
public class ResultException extends Exception implements IResult {
public final Throwable exception;
public final String stdout;
public final String stderr;
public ResultException(Throwable exception, String stdout, String stderr) {
super(exception);
this.exception = exception;
if (stdout.endsWith("\n")) {
stdout = stdout.substring(0, stdout.length() - 1);
}
if (stderr.endsWith("\n")) {
stderr = stderr.substring(0, stderr.length() - 1);
}
this.stdout = stdout.trim();
this.stderr = stderr.trim();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("============================\n");
sb.append("Exception was '" + exception.getClass() + "'\n");
sb.append("Exception msg '" + exception.getMessage() + "'\n");
sb.append(getStackTraceAsString());
sb.append("STDOUT was\n" + stdout + "\n");
sb.append("STDERR was\n" + stderr + "\n");
sb.append("============================\n");
return sb.toString();
}
public String getStackTraceAsString() {
StringWriter stringWriter = new StringWriter();
exception.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
public Throwable getDeepestCause() {
Throwable cause = exception;
while (cause.getCause() != null) {
cause = cause.getCause();
}
return cause;
}
private int getNestingLevel() {
int nestingLevel = 0;
Throwable cause = exception;
while (cause.getCause() != null) {
nestingLevel++;
cause = cause.getCause();
}
return nestingLevel;
}
/**
* This equals method is used by {@link GenerativeSpringLoadedTest}s to determine whether the behavior observed on
* regular Java and SpringLoaded are accepted as sufficiently equivalent to pass the test.
*
*/
@Override
public boolean equals(Object obj) {
//1) We are both ResultExpeptions!
if (obj.getClass() != this.getClass()) {
return false;
}
ResultException other = (ResultException) obj;
//2) We are the same type of Exception
if (other.exception.getClass() != this.exception.getClass()) {
return false;
}
//3) We have the same nesting depth
if (other.getNestingLevel() != this.getNestingLevel()) {
return false;
}
//4) Deepest causes are of same type
Throwable myCause = this.getDeepestCause();
Throwable otherCause = other.getDeepestCause();
if (myCause.getClass() != otherCause.getClass()) {
return false;
}
//5) Deepest causes have same message
if (myCause.getMessage() == null) {
return otherCause.getMessage() == null;
}
return myCause.getMessage().equals(otherCause.getMessage());
}
@Override
public int hashCode() {
Throwable e = getDeepestCause();
int hash = e.getClass().hashCode();
return hash;
}
public String getSummary() {
Throwable cause = getDeepestCause();
return cause.getClass().getSimpleName() + " " + cause.getMessage();
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springsource.loaded.testgen.GenerativeSpringLoadedTest;
/**
* Captures the exception raised by executing a method: the exception the stdout and the stderr.
*
* @author Kris De Volder
* @since 1.0
*/
@SuppressWarnings("serial")
public class ResultException extends Exception implements IResult {
public final Throwable exception;
public final String stdout;
public final String stderr;
public ResultException(Throwable exception, String stdout, String stderr) {
super(exception);
this.exception = exception;
if (stdout.endsWith("\n")) {
stdout = stdout.substring(0, stdout.length() - 1);
}
if (stderr.endsWith("\n")) {
stderr = stderr.substring(0, stderr.length() - 1);
}
this.stdout = stdout.trim();
this.stderr = stderr.trim();
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("============================\n");
sb.append("Exception was '" + exception.getClass() + "'\n");
sb.append("Exception msg '" + exception.getMessage() + "'\n");
sb.append(getStackTraceAsString());
sb.append("STDOUT was\n" + stdout + "\n");
sb.append("STDERR was\n" + stderr + "\n");
sb.append("============================\n");
return sb.toString();
}
public String getStackTraceAsString() {
StringWriter stringWriter = new StringWriter();
exception.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
public Throwable getDeepestCause() {
Throwable cause = exception;
while (cause.getCause() != null) {
cause = cause.getCause();
}
return cause;
}
private int getNestingLevel() {
int nestingLevel = 0;
Throwable cause = exception;
while (cause.getCause() != null) {
nestingLevel++;
cause = cause.getCause();
}
return nestingLevel;
}
/**
* This equals method is used by {@link GenerativeSpringLoadedTest}s to determine whether the behavior observed on
* regular Java and SpringLoaded are accepted as sufficiently equivalent to pass the test.
*
*/
@Override
public boolean equals(Object obj) {
//1) We are both ResultExpeptions!
if (obj.getClass() != this.getClass()) {
return false;
}
ResultException other = (ResultException) obj;
//2) We are the same type of Exception
if (other.exception.getClass() != this.exception.getClass()) {
return false;
}
//3) We have the same nesting depth
if (other.getNestingLevel() != this.getNestingLevel()) {
return false;
}
//4) Deepest causes are of same type
Throwable myCause = this.getDeepestCause();
Throwable otherCause = other.getDeepestCause();
if (myCause.getClass() != otherCause.getClass()) {
return false;
}
//5) Deepest causes have same message
if (myCause.getMessage() == null) {
return otherCause.getMessage() == null;
}
return myCause.getMessage().equals(otherCause.getMessage());
}
@Override
public int hashCode() {
Throwable e = getDeepestCause();
int hash = e.getClass().hashCode();
return hash;
}
public String getSummary() {
Throwable cause = getDeepestCause();
return cause.getClass().getSimpleName() + " " + cause.getMessage();
}
}

View File

@@ -1,80 +1,80 @@
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
public class TestClassLoader extends URLClassLoader {
private static int idCt = 0;
private int id = idCt++;
public TestClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected synchronized Class<?> loadClass(String arg0, boolean arg1) throws ClassNotFoundException {
// System.out.println(this+" being asked to load class "+arg0+","+arg1);
return super.loadClass(arg0, arg1);
}
@Override
public URL getResource(String name) {
// System.out.println(this+" being asked to getResource "+name);
return super.getResource(name);
}
@Override
public URL findResource(String arg0) {
// System.out.println(this+" being asked to find resource "+arg0);
return super.findResource(arg0);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// System.out.println(this+" being asked to find class "+name);
return super.findClass(name);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// System.out.println(this + " loading "+name);
return super.loadClass(name);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
// System.out.println(this+" being asked to find resources "+name);
return super.findResources(name);
}
public Class<?> defineTheClass(String name, byte[] bytes) {
// System.out.println(this + " defining "+name);
return super.defineClass(name, bytes, 0, bytes.length);
}
@Override
public String toString() {
return "ClassLoader( " + id + " )";
}
}
/*
* Copyright 2010-2012 VMware and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springsource.loaded.test.infra;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
public class TestClassLoader extends URLClassLoader {
private static int idCt = 0;
private int id = idCt++;
public TestClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
protected synchronized Class<?> loadClass(String arg0, boolean arg1) throws ClassNotFoundException {
// System.out.println(this+" being asked to load class "+arg0+","+arg1);
return super.loadClass(arg0, arg1);
}
@Override
public URL getResource(String name) {
// System.out.println(this+" being asked to getResource "+name);
return super.getResource(name);
}
@Override
public URL findResource(String arg0) {
// System.out.println(this+" being asked to find resource "+arg0);
return super.findResource(arg0);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// System.out.println(this+" being asked to find class "+name);
return super.findClass(name);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// System.out.println(this + " loading "+name);
return super.loadClass(name);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
// System.out.println(this+" being asked to find resources "+name);
return super.findResources(name);
}
public Class<?> defineTheClass(String name, byte[] bytes) {
// System.out.println(this + " defining "+name);
return super.defineClass(name, bytes, 0, bytes.length);
}
@Override
public String toString() {
return "ClassLoader( " + id + " )";
}
}