use of javassist.bytecode.BadBytecode in project fakereplace by fakereplace.
the class FakeMethodCallManipulator method handleFakeMethodCall.
private void handleFakeMethodCall(ClassFile file, Set<MethodInfo> modifiedMethods, MethodInfo m, CodeIterator it, int index, int op, Data data) throws BadBytecode {
// NOP out the whole thing
it.writeByte(CodeIterator.NOP, index);
it.writeByte(CodeIterator.NOP, index + 1);
it.writeByte(CodeIterator.NOP, index + 2);
if (op == CodeIterator.INVOKEINTERFACE) {
// INVOKEINTERFACE has some extra parameters
it.writeByte(CodeIterator.NOP, index + 3);
it.writeByte(CodeIterator.NOP, index + 4);
}
// now we write some bytecode to invoke it directly
final boolean staticMethod = data.getType() == Type.STATIC;
Bytecode byteCode = new Bytecode(file.getConstPool());
// stick the method number in the const pool then load it onto the
// stack
ManipulationUtils.pushParametersIntoArray(byteCode, data.getMethodDesc());
int scind = file.getConstPool().addIntegerInfo(data.getMethodNumber());
byteCode.addLdc(scind);
byteCode.add(Opcode.SWAP);
// invoke the added method
if (staticMethod) {
byteCode.addInvokestatic(data.getClassName(), Constants.ADDED_STATIC_METHOD_NAME, "(I[Ljava/lang/Object;)Ljava/lang/Object;");
} else if (data.getType() == Type.INTERFACE) {
byteCode.addInvokeinterface(data.getClassName(), Constants.ADDED_METHOD_NAME, "(I[Ljava/lang/Object;)Ljava/lang/Object;", 3);
} else {
byteCode.addInvokevirtual(data.getClassName(), Constants.ADDED_METHOD_NAME, "(I[Ljava/lang/Object;)Ljava/lang/Object;");
}
// cast it to the appropriate type and return it
String returnType = DescriptorUtils.getReturnType(data.getMethodDesc());
if (returnType.length() == 1 && !returnType.equals("V")) {
Boxing.unbox(byteCode, returnType.charAt(0));
} else if (returnType.equals("V")) {
byteCode.add(Opcode.POP);
} else {
byteCode.addCheckcast(returnType.substring(1, returnType.length() - 1));
}
it.insertEx(byteCode.get());
modifiedMethods.add(m);
}
use of javassist.bytecode.BadBytecode in project fakereplace by fakereplace.
the class MainTransformer method transform.
@Override
public byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException {
if (className == null) {
// TODO: deal with lambdas
return classfileBuffer;
}
boolean replaceable = Fakereplace.isClassReplaceable(className, loader);
if (classBeingRedefined != null) {
retransformationStarted = true;
if (logClassRetransformation && replaceable) {
log.info("Fakereplace is replacing class " + className);
}
}
ChangedClassImpl changedClass = null;
if (classBeingRedefined != null) {
changedClass = new ChangedClassImpl(classBeingRedefined, classfileBuffer);
}
boolean changed = false;
if (!replaceable && UnmodifiedFileIndex.isClassUnmodified(className)) {
return null;
}
Set<Class<?>> classesToRetransform = new HashSet<>();
final ClassFile file;
try {
Set<MethodInfo> modifiedMethods = new HashSet<>();
file = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer)));
for (final FakereplaceTransformer transformer : transformers) {
if (transformer.transform(loader, className, classBeingRedefined, protectionDomain, file, classesToRetransform, changedClass, modifiedMethods, replaceable)) {
changed = true;
}
}
if (!changed) {
UnmodifiedFileIndex.markClassUnmodified(className);
return null;
} else {
try {
if (!modifiedMethods.isEmpty()) {
ClassPool classPool = new ClassPool();
if (loader == null) {
classPool.appendClassPath(new LoaderClassPath(ClassLoader.getSystemClassLoader()));
} else {
classPool.appendClassPath(new LoaderClassPath(loader));
}
classPool.appendSystemPath();
for (MethodInfo method : modifiedMethods) {
if (method.getCodeAttribute() != null) {
method.getCodeAttribute().computeMaxStack();
try {
method.rebuildStackMap(classPool);
} catch (BadBytecode e) {
Throwable root = e;
while (!(root instanceof NotFoundException) && root != null && root.getCause() != root) {
root = root.getCause();
}
if (root instanceof NotFoundException) {
NotFoundException cause = (NotFoundException) root;
Bytecode bytecode = new Bytecode(file.getConstPool());
bytecode.addNew(NoClassDefFoundError.class.getName());
bytecode.add(Opcode.DUP);
bytecode.addLdc(cause.getMessage());
bytecode.addInvokespecial(NoClassDefFoundError.class.getName(), "<init>", "(Ljava/lang/String;)V");
bytecode.add(Opcode.ATHROW);
method.setCodeAttribute(bytecode.toCodeAttribute());
method.getCodeAttribute().computeMaxStack();
method.getCodeAttribute().setMaxLocals(DescriptorUtils.maxLocalsFromParameters(method.getDescriptor()) + 1);
method.rebuildStackMap(classPool);
} else {
throw e;
}
}
}
}
}
} catch (BadBytecode e) {
ByteArrayOutputStream bs = new ByteArrayOutputStream();
file.write(new DataOutputStream(bs));
// dump the class for debugging purposes
final String dumpDir = AgentOptions.getOption(AgentOption.DUMP_DIR);
if (dumpDir != null) {
try {
File dump = new File(dumpDir + '/' + file.getName() + "$FAILED.class");
dump.getParentFile().mkdirs();
FileOutputStream s = new FileOutputStream(dump);
DataOutputStream dos = new DataOutputStream(s);
file.write(dos);
s.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
throw new RuntimeException(e);
}
ByteArrayOutputStream bs = new ByteArrayOutputStream();
file.write(new DataOutputStream(bs));
// dump the class for debugging purposes
final String dumpDir = AgentOptions.getOption(AgentOption.DUMP_DIR);
if (dumpDir != null) {
try {
File dump = new File(dumpDir + '/' + file.getName() + ".class");
dump.getParentFile().mkdirs();
FileOutputStream s = new FileOutputStream(dump);
DataOutputStream dos = new DataOutputStream(s);
file.write(dos);
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (!classesToRetransform.isEmpty()) {
synchronized (this) {
retransformationOutstandingCount++;
}
Thread t = new Thread(() -> {
try {
Fakereplace.getInstrumentation().retransformClasses(classesToRetransform.toArray(new Class[classesToRetransform.size()]));
} catch (UnmodifiableClassException e) {
log.error("Failed to retransform classes", e);
} finally {
synchronized (MainTransformer.this) {
retransformationOutstandingCount--;
notifyAll();
}
}
});
t.setDaemon(true);
t.start();
}
if (classBeingRedefined != null) {
changedClasses.add(changedClass);
queueIntegration();
}
return bs.toByteArray();
}
} catch (IOException e) {
e.printStackTrace();
throw new IllegalClassFormatException(e.getMessage());
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
use of javassist.bytecode.BadBytecode in project fakereplace by fakereplace.
the class ClassInfoTransformer method transform.
@Override
public boolean transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, ClassFile file, Set<Class<?>> classesToRetransform, ChangedClassImpl changedClass, Set<MethodInfo> modifiedMethods, boolean replaceable) throws IllegalClassFormatException, BadBytecode, DuplicateMemberException {
if (className.equals("com/sun/beans/introspect/ClassInfo")) {
file.addInterface(Runnable.class.getName());
MethodInfo run = new MethodInfo(file.getConstPool(), "run", "()V");
run.setAccessFlags(AccessFlag.PUBLIC);
Bytecode b = new Bytecode(file.getConstPool(), 1, 1);
b.addGetstatic(file.getName(), "CACHE", "Lcom/sun/beans/util/Cache;");
b.addInvokevirtual("com/sun/beans/util/Cache", "clear", "()V");
b.add(Opcode.RETURN);
run.setCodeAttribute(b.toCodeAttribute());
file.addMethod(run);
MethodInfo m = file.getMethod("<init>");
CodeIterator iterator = m.getCodeAttribute().iterator();
int pos = 0;
while (iterator.hasNext()) {
pos = iterator.next();
}
b = new Bytecode(file.getConstPool(), 1, 0);
b.add(Bytecode.ALOAD_0);
b.addPutstatic(ClassInfoTransformer.class.getName(), "clearAction", "Ljava/lang/Runnable;");
iterator.insert(pos, b.get());
m.getCodeAttribute().computeMaxStack();
return true;
}
return false;
}
use of javassist.bytecode.BadBytecode in project fakereplace by fakereplace.
the class ClassLoaderInstrumentation method redefineClassLoader.
/**
* This method instruments class loaders so that they can load our helper
* classes.
*/
@SuppressWarnings("unchecked")
public static boolean redefineClassLoader(ClassFile classFile, Set<MethodInfo> modifiedMethods) throws BadBytecode {
boolean modified = false;
for (MethodInfo method : (List<MethodInfo>) classFile.getMethods()) {
if (Modifier.isStatic(method.getAccessFlags())) {
continue;
}
if (method.getName().equals("loadClass") && (method.getDescriptor().equals("(Ljava/lang/String;)Ljava/lang/Class;") || method.getDescriptor().equals("(Ljava/lang/String;Z)Ljava/lang/Class;"))) {
modifiedMethods.add(method);
modified = true;
if (method.getCodeAttribute().getMaxLocals() < 4) {
method.getCodeAttribute().setMaxLocals(4);
}
// now we instrument the loadClass
// if the system requests a class from the generated class package
// then
// we check to see if it is already loaded.
// if not we try and get the class definition from GlobalData
// we do not need to delegate as GlobalData will only
// return the data to the correct classloader.
// if the data is not null then we define the class, link
// it if requested and return it.
final CodeIterator iterator = method.getCodeAttribute().iterator();
final Bytecode b = new Bytecode(classFile.getConstPool());
b.addAload(1);
b.addAload(0);
b.addInvokestatic(ClassLookupManager.class.getName(), "getClassData", "(Ljava/lang/String;Ljava/lang/Object;)[B");
b.add(Opcode.DUP);
b.add(Opcode.IFNULL);
JumpMarker jumpEnd = JumpUtils.addJumpInstruction(b);
// now we need to do the findLoadedClasses thing
b.addAload(0);
b.addAload(1);
b.addInvokevirtual("java.lang.ClassLoader", "findLoadedClass", "(Ljava/lang/String;)Ljava/lang/Class;");
b.add(Opcode.DUP);
b.add(Opcode.IFNULL);
JumpMarker notFound = JumpUtils.addJumpInstruction(b);
b.add(Opcode.ARETURN);
notFound.mark();
b.add(Opcode.POP);
b.addAstore(3);
b.addAload(0);
b.addAload(1);
b.addAload(3);
b.addIconst(0);
b.addAload(3);
b.add(Opcode.ARRAYLENGTH);
b.addInvokevirtual("java.lang.ClassLoader", "defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;");
if (method.getDescriptor().equals("Ljava/lang/String;Z)Ljava/lang/Class;")) {
b.addIload(2);
} else {
b.addIconst(0);
}
b.add(Opcode.IFEQ);
final JumpMarker linkJumpEnd = JumpUtils.addJumpInstruction(b);
b.add(Opcode.DUP);
b.addAload(0);
b.add(Opcode.SWAP);
b.addInvokevirtual("java.lang.ClassLoader", "resolveClass", "(Ljava/lang/Class;)V");
linkJumpEnd.mark();
b.add(Opcode.ARETURN);
jumpEnd.mark();
b.add(Opcode.POP);
if (!classFile.getName().startsWith("java.") && !classFile.getName().startsWith("com.sun") && !classFile.getName().startsWith("sun") && !classFile.getName().startsWith("jdk.internal")) {
// now we need to check if this is a fakereplace class
// and if so always delegate to the appropriate loader
b.addAload(1);
b.addLdc("org.fakereplace");
b.addInvokevirtual(String.class.getName(), "startsWith", "(Ljava/lang/String;)Z");
b.add(Opcode.IFEQ);
JumpMarker notFakereplace = JumpUtils.addJumpInstruction(b);
// so this is a fakereplace class, delegate to the system loader
b.addInvokestatic(ClassLoader.class.getName(), "getSystemClassLoader", "()Ljava/lang/ClassLoader;");
b.addAload(1);
b.addInvokevirtual(ClassLoader.class.getName(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
b.add(Opcode.ARETURN);
notFakereplace.mark();
}
iterator.insert(b.get());
method.getCodeAttribute().computeMaxStack();
}
}
return modified;
}
use of javassist.bytecode.BadBytecode in project fakereplace by fakereplace.
the class ResteasyTransformer method transform.
@Override
public boolean transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final ClassFile file, Set<Class<?>> classesToRetransform, ChangedClassImpl changedClass, Set<MethodInfo> modifiedMethods, boolean replaceable) throws IllegalClassFormatException, BadBytecode {
try {
if (file.getName().equals(ResteasyExtension.FILTER_DISPATCHER)) {
FieldInfo field = new FieldInfo(file.getConstPool(), FIELD_NAME, FILTER_FIELD_TYPE);
field.setAccessFlags(Modifier.PUBLIC);
file.addField(field);
field = new FieldInfo(file.getConstPool(), PARAMETER_FIELD_NAME, SET_TYPE);
field.setAccessFlags(Modifier.PUBLIC);
file.addField(field);
for (final MethodInfo method : (List<MethodInfo>) file.getMethods()) {
if (method.getName().equals("init") && method.getDescriptor().equals("(Ljavax/servlet/FilterConfig;)V")) {
method.setAccessFlags(method.getAccessFlags() | AccessFlag.SYNCHRONIZED);
modifiedMethods.add(method);
final Bytecode b = new Bytecode(file.getConstPool());
b.addAload(0);
b.addNew(RESTEASY_FILTER_CONFIG);
b.add(Opcode.DUP);
b.addAload(1);
b.addInvokespecial(RESTEASY_FILTER_CONFIG, "<init>", "(Ljavax/servlet/FilterConfig;)V");
b.addPutfield(ResteasyExtension.FILTER_DISPATCHER, FIELD_NAME, FILTER_FIELD_TYPE);
b.addAload(1);
b.addInvokeinterface("javax/servlet/FilterConfig", "getServletContext", "()Ljavax/servlet/ServletContext;", 1);
b.addAload(0);
b.addGetfield(ResteasyExtension.FILTER_DISPATCHER, PARAMETER_FIELD_NAME, SET_TYPE);
b.addInvokestatic(CONTEXT_PARAMS, "init", INIT_METHOD_DESC);
b.addAload(0);
b.add(Opcode.SWAP);
b.addPutfield(ResteasyExtension.FILTER_DISPATCHER, PARAMETER_FIELD_NAME, SET_TYPE);
method.getCodeAttribute().iterator().insert(b.get());
method.getCodeAttribute().computeMaxStack();
} else if (method.getName().equals("<init>")) {
// no idea why this is needed
method.getCodeAttribute().setMaxStack(1);
} else if (method.getName().equals("getDispatcher")) {
method.setAccessFlags(method.getAccessFlags() | AccessFlag.SYNCHRONIZED);
modifiedMethods.add(method);
}
}
return true;
} else if (file.getName().equals(ResteasyExtension.SERVLET_DISPATCHER)) {
FieldInfo field = new FieldInfo(file.getConstPool(), FIELD_NAME, SERVLET_FIELD_TYPE);
field.setAccessFlags(Modifier.PUBLIC);
file.addField(field);
field = new FieldInfo(file.getConstPool(), PARAMETER_FIELD_NAME, SET_TYPE);
field.setAccessFlags(Modifier.PUBLIC);
file.addField(field);
for (final MethodInfo method : (List<MethodInfo>) file.getMethods()) {
if (method.getName().equals("init") && method.getDescriptor().equals("(Ljavax/servlet/ServletConfig;)V")) {
method.setAccessFlags(method.getAccessFlags() | AccessFlag.SYNCHRONIZED);
modifiedMethods.add(method);
final Bytecode b = new Bytecode(file.getConstPool());
b.addAload(0);
b.addNew(RESTEASY_SERVLET_CONFIG);
b.add(Opcode.DUP);
b.addAload(1);
b.addInvokespecial(RESTEASY_SERVLET_CONFIG, "<init>", "(Ljavax/servlet/ServletConfig;)V");
b.addPutfield(ResteasyExtension.SERVLET_DISPATCHER, FIELD_NAME, SERVLET_FIELD_TYPE);
b.addAload(1);
b.addInvokeinterface("javax/servlet/ServletConfig", "getServletContext", "()Ljavax/servlet/ServletContext;", 1);
b.addAload(0);
b.addGetfield(ResteasyExtension.SERVLET_DISPATCHER, PARAMETER_FIELD_NAME, SET_TYPE);
b.addInvokestatic(CONTEXT_PARAMS, "init", INIT_METHOD_DESC);
b.addAload(0);
b.add(Opcode.SWAP);
b.addPutfield(ResteasyExtension.SERVLET_DISPATCHER, PARAMETER_FIELD_NAME, SET_TYPE);
method.getCodeAttribute().iterator().insert(b.get());
method.getCodeAttribute().computeMaxStack();
} else if (method.getName().equals("<init>")) {
method.getCodeAttribute().setMaxStack(1);
} else if (method.getName().equals("getDispatcher")) {
method.setAccessFlags(method.getAccessFlags() | AccessFlag.SYNCHRONIZED);
modifiedMethods.add(method);
}
}
return true;
}
} catch (DuplicateMemberException e) {
throw new RuntimeException(e);
}
return false;
}
Aggregations