/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.compiler;
import gw.config.CommonServices;
import gw.internal.gosu.ir.TransformingCompiler;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.ICompilableType;
import gw.lang.reflect.gs.IGosuClassLoader;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.module.TypeSystemLockHelper;
import gw.util.concurrent.ConcurrentWeakValueHashMap;
import java.util.Map;
public class SingleServingGosuClassLoader extends ClassLoader implements IGosuClassLoader
{
private static final Map<String, Class> CACHE = new ConcurrentWeakValueHashMap<String, Class>();
private GosuClassLoader _parent;
public static Class getCached( ICompilableType gsClass ) {
return CACHE.get( gsClass.getName() );
}
public static void clearCache() {
CACHE.clear();
}
SingleServingGosuClassLoader( GosuClassLoader parent )
{
super( parent.getActualLoader() );
_parent = parent;
if( CommonServices.getPlatformHelper().isInIDE() ) {
//## todo: uncomment
//throw new IllegalStateException( "Class loading with single-serving loader is probably wrong in the IDE" );
}
}
public Class<?> findClass( String strName ) throws ClassNotFoundException
{
Class cls = CACHE.get( strName.replace( '$', '.' ) );
if( cls != null ) {
return cls;
}
return _parent.findClass( strName );
}
@Override
protected Class<?> loadClass( String name, boolean resolve ) throws ClassNotFoundException {
// Acquire the type system lock and this loader's lock in a consistent order to prevent deadlock.
// Note this is only important here for the case where the parent loader of this loader
// loads a gosu class that is turn needs to load
TypeSystemLockHelper.getTypeSystemLockWithMonitor( this );
try {
return super.loadClass( name, resolve );
}
finally {
TypeSystem.unlock();
}
}
@Override
public void dumpAllClasses() {
// Do nothing here: it should be taken care of by the main classloader
}
Class _defineClass( ICompilableType gsClass )
{
Class cls = getCached( gsClass );
if( cls != null )
{
return cls;
}
byte[] classBytes = compileClass( gsClass, _parent.shouldDebugClass( gsClass ) );
CompilationStatistics.instance().collectStats( gsClass, classBytes, true );
if( classBytes == null )
{
throw new IllegalStateException( "Could not generate class for " + gsClass.getName() );
}
String strPackage = gsClass.getNamespace();
if( getPackage( strPackage ) == null )
{
definePackage( strPackage, null, null, null, null, null, null, null );
}
cls = defineClass( GosuClassLoader.getJavaName( gsClass ), classBytes, 0, classBytes.length );
CACHE.put( gsClass.getName(), cls );
return cls;
}
private byte[] compileClass( ICompilableType type, boolean debug )
{
return TransformingCompiler.compileClass( type, debug );
}
@Override
public IJavaType getFunctionClassForArity(int length)
{
return null;
}
@Override
public ClassLoader getActualLoader() {
return this;
}
@Override
public Class defineClass(String name, byte[] bytes) {
return super.defineClass(name, bytes, 0, bytes.length);
}
@Override
public byte[] getBytes(ICompilableType gsClass)
{
return compileClass( gsClass, false );
}
@Override
public String getInterfaceMethodsClassName( ICompilableType gsClass ) {
return null;
}
@Override
public void assignParent( ClassLoader classLoader )
{
throw new UnsupportedOperationException( "Should not happen" );
}
public boolean isDisposed()
{
return false;
}
}