org.springsource.loaded.agent.GroovyPlugin Maven / Gradle / Ivy
/*
* 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.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.springsource.loaded.LoadtimeInstrumentationPlugin;
import org.springsource.loaded.ReloadEventProcessorPlugin;
/**
* What does it do?
*
* So far the GroovyPlugin can do two different things - configurable through the 'allowCompilableCallSites' flag.
*
*
* If the flag is false: The plugin intercepts two of the main system types in groovy and turns OFF call site
* compilation. Without this compilation the compiler will not be generating classes, it will instead be using
* reflection all the time. This is simpler to handle (as we intercept reflection) but performance == thesuck.
*
* If the flag is true: The plugin leaves groovy to compile call sites. We intercept the define method in the
* classloader used to define these generated call site classes and ensure they are rewritten correctly. Note there is
* an alternative here of getting the SpringLoadedPreProcessor to recognize these special classloaders and just
* instrument them that way. However, if we let the plugin do it it is easier to test!
*
* To see the difference in these approaches, check the numbers in the Groovy Benchmark tests.
*
* @author Andy Clement
* @since 0.7.0
*/
public class GroovyPlugin implements LoadtimeInstrumentationPlugin, ReloadEventProcessorPlugin {
boolean allowCompilableCallSites = true;
// GroovySunClassLoader - can make the final field non final so it can be set to null (it is checked as part of the isCompilable methodin the callsitegenerator)
// CallSiteGenerator - make isCompilable return false, which means we will never generate a direct call to a method that may not yet be on the target
// implementing LoadtimeInstrumentationPlugin
public boolean accept(String slashedTypeName, ClassLoader classLoader, ProtectionDomain protectionDomain,
byte[] bytes) {
// TODO take classloader into account?
if (slashedTypeName == null) {
return false;
}
if (!allowCompilableCallSites) {
return slashedTypeName.equals("org/codehaus/groovy/runtime/callsite/GroovySunClassLoader")
|| slashedTypeName.equals("org/codehaus/groovy/runtime/callsite/CallSiteGenerator");
}
else {
if (slashedTypeName.equals("org/codehaus/groovy/reflection/ClassLoaderForClassArtifacts")) {
return true;
}
}
return false;
}
public byte[] modify(String slashedClassName, ClassLoader classLoader, byte[] bytes) {
if (allowCompilableCallSites) {
return modifyDefineInClassLoaderForClassArtifacts(bytes);
}
else {
// Deactivate compilation
if (slashedClassName.equals("org/codehaus/groovy/runtime/callsite/GroovySunClassLoader")) {
// if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) {
// log.info("loadtime modifying " + slashedClassName);
// }
ClassReader cr = new ClassReader(bytes);
NonFinalizer ca = new NonFinalizer("sunVM");
// ClassVisitingConstructorAppender ca = new ClassVisitingConstructorAppender("org/springsource/loaded/agent/SpringPlugin",
// "recordInstance");
cr.accept(ca, 0);
byte[] newbytes = ca.getBytes();
return newbytes;
}
else {
// must be the CallSiteGenerator
ClassReader cr = new ClassReader(bytes);
FalseReturner ca = new FalseReturner("isCompilable");
cr.accept(ca, 0);
byte[] newbytes = ca.getBytes();
return newbytes;
}
}
}
private byte[] modifyDefineInClassLoaderForClassArtifacts(byte[] bytes) {
ClassReader cr = new ClassReader(bytes);
ModifyDefineInClassLoaderForClassArtifactsType ca = new ModifyDefineInClassLoaderForClassArtifactsType();
cr.accept(ca, 0);
byte[] newbytes = ca.getBytes();
return newbytes;
}
// called by the modified code
public static void recordInstance(Object obj) {
}
// implementing CallbackPlugin
public void reloadEvent(String typename, Class> clazz, String versionsuffix) {
}
public boolean shouldRerunStaticInitializer(String typename, Class> clazz, String encodedTimestamp) {
return false;
}
}