gw.lang.init.GosuRuntimeManifoldHost Maven / Gradle / Ivy
package gw.lang.init;
import gw.fs.IDirectory;
import gw.fs.IResource;
import gw.fs.jar.IJarFileDirectory;
import gw.lang.Gosu;
import gw.lang.reflect.TypeSystem;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import manifold.internal.host.RuntimeManifoldHost;
import manifold.internal.javac.JavaParser;
import manifold.util.NecessaryEvilUtil;
public class GosuRuntimeManifoldHost extends RuntimeManifoldHost
{
// override RuntimeManifoldHost to use a single instance since it is always used in the context of the type sys lock
private JavaParser _javaParser;
static
{
NecessaryEvilUtil.bypassJava9Security();
}
public static GosuRuntimeManifoldHost get()
{
return (GosuRuntimeManifoldHost)RuntimeManifoldHost.get();
}
public GosuRuntimeManifoldHost()
{
// System.out.println( getClass().getTypeName() );
}
@Override
public ClassLoader getActualClassLoader()
{
return TypeSystem.getGosuClassLoader().getActualLoader();
}
@Override
public JavaParser getJavaParser()
{
return _javaParser == null ? _javaParser = new JavaParser( this ) : _javaParser;
}
@Override
public void init( List sourcepath, List classpath )
{
if( Gosu.bootstrapGosuWhenInitiatedViaClassfile() ) {
// Assuming we are in runtime, we push the root module in the case where the process was started with java.exe and not gosu.cmd
// In other words a Gosu class can be loaded directly from classfile in a bare bones Java program where only the Gosu runtime is
// on the classpath and no module was pushed prior to loading.
TypeSystem.pushModule( TypeSystem.getGlobalModule() );
}
if( classpath.isEmpty() )
{
classpath = new ArrayList<>();
}
classpath.addAll( removeNonFiles( TypeSystem.getGlobalModule().getJavaClassPath() ) );
if( sourcepath.isEmpty() )
{
sourcepath = new ArrayList<>();
}
sourcepath.addAll( removeNonFiles( TypeSystem.getGlobalModule().getSourcePath() ) );
if( !sourcepath.isEmpty() )
{
// paths are set explicitly, initialize directly from them
initDirectly( sourcepath, classpath );
return;
}
// paths are empty, init includes classpath from classloader
super.init( sourcepath, classpath );
}
// Override the base class to filter out the intellij classpath jar because WindowsPathParser otherwise chokes
// on the jre paths having an illegal '!' character in them. Note this is a total hack and should be fixed on
// the Studio end, but fixing this on the Gosu side for now.
@Override
protected List createDefaultClassPath()
{
// filter out the intellij classpath
return super.createDefaultClassPath().stream()
.filter( f -> !isIntelliJGeneratedClasspathJar( f ) )
.collect( Collectors.toList() );
}
private List removeNonFiles( List dirs )
{
return dirs.stream()
.filter( IResource::isJavaFile )
.filter( e -> !isIntelliJGeneratedClasspathJar( e ) )
.map( IResource::toJavaFile )
.collect( Collectors.toList() );
}
// IntelliJ generates a classpath jar file when Studio runs a Gosu program/scratchpad, this
// not only duplicate what we have in the classpath already, but it also exposes a bug in the
// JVM's WindowsClassParser involving unexpected URL formatted paths instead of file system
// formatted paths. This code is a hack to identify such a classpath jar file so it can be
// removed from the classpath.
private boolean isIntelliJGeneratedClasspathJar( IDirectory e )
{
if( e instanceof IJarFileDirectory )
{
String name = e.getName();
return name.startsWith( "classpath" ) &&
Character.isDigit( name.charAt( "classpath".length() ) );
}
return false;
}
private boolean isIntelliJGeneratedClasspathJar( manifold.api.fs.IDirectory e )
{
if( e instanceof manifold.api.fs.jar.IJarFileDirectory )
{
String name = e.getName();
return name.startsWith( "classpath" ) &&
Character.isDigit( name.charAt( "classpath".length() ) );
}
return false;
}
}