More cleanup

This commit is contained in:
Anuken 2019-12-09 16:59:43 -05:00
parent 0078a8cb8e
commit 2a03ef0c43
8 changed files with 231 additions and 330 deletions

View File

@ -19,7 +19,6 @@ import io.anuke.arc.util.serialization.*;
import io.anuke.mindustry.game.Saves.*;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.mod.*;
import io.anuke.mindustry.rhino.*;
import io.anuke.mindustry.ui.dialogs.*;
import java.io.*;
@ -68,7 +67,7 @@ public class AndroidLauncher extends AndroidApplication{
@Override
public org.mozilla.javascript.Context getScriptContext(){
return new RhinoBuilder(getContext()).enterContext();
return AndroidRhinoContext.enterContext(getContext().getCacheDir());
}
@Override

View File

@ -0,0 +1,229 @@
package io.anuke.mindustry;
import android.annotation.*;
import android.os.*;
import com.android.dex.*;
import com.android.dx.cf.direct.*;
import com.android.dx.command.dexer.*;
import com.android.dx.dex.*;
import com.android.dx.dex.cf.*;
import com.android.dx.dex.file.DexFile;
import com.android.dx.merge.*;
import dalvik.system.*;
import io.anuke.arc.*;
import io.anuke.arc.backends.android.surfaceview.*;
import io.anuke.arc.util.ArcAnnotate.NonNull;
import io.anuke.arc.util.ArcAnnotate.Nullable;
import io.anuke.mindustry.AndroidRhinoContext.BaseAndroidClassLoader.*;
import org.mozilla.javascript.*;
import java.io.*;
import java.nio.*;
/**
* Helps to prepare a Rhino Context for usage on android.
* @author F43nd1r
* @since 11.01.2016
*/
public class AndroidRhinoContext{
/**
* call this instead of {@link Context#enter()}
* @return a context prepared for android
*/
public static Context enterContext(File cacheDirectory){
if(!SecurityController.hasGlobal())
SecurityController.initGlobal(new SecurityController(){
@Override
public GeneratedClassLoader createClassLoader(ClassLoader classLoader, Object o){
return Context.getCurrentContext().createClassLoader(classLoader);
}
@Override
public Object getDynamicSecurityDomain(Object o){
return null;
}
});
AndroidContextFactory factory;
if(!ContextFactory.hasExplicitGlobal()){
factory = new AndroidContextFactory(cacheDirectory);
ContextFactory.getGlobalSetter().setContextFactoryGlobal(factory);
}else if(!(ContextFactory.getGlobal() instanceof AndroidContextFactory)){
throw new IllegalStateException("Cannot initialize factory for Android Rhino: There is already another factory");
}else{
factory = (AndroidContextFactory)ContextFactory.getGlobal();
}
return factory.enterContext();
}
/**
* Ensures that the classLoader used is correct
* @author F43nd1r
* @since 11.01.2016
*/
public static class AndroidContextFactory extends ContextFactory{
private final File cacheDirectory;
/**
* Create a new factory. It will cache generated code in the given directory
* @param cacheDirectory the cache directory
*/
public AndroidContextFactory(File cacheDirectory){
this.cacheDirectory = cacheDirectory;
initApplicationClassLoader(createClassLoader(AndroidContextFactory.class.getClassLoader()));
}
/**
* Create a ClassLoader which is able to deal with bytecode
* @param parent the parent of the create classloader
* @return a new ClassLoader
*/
@Override
public BaseAndroidClassLoader createClassLoader(ClassLoader parent){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
return new InMemoryAndroidClassLoader(parent);
}
return new FileAndroidClassLoader(parent, cacheDirectory);
}
@Override
protected void onContextReleased(final Context cx){
super.onContextReleased(cx);
((BaseAndroidClassLoader)cx.getApplicationClassLoader()).reset();
}
}
/**
* Compiles java bytecode to dex bytecode and loads it
* @author F43nd1r
* @since 11.01.2016
*/
abstract static class BaseAndroidClassLoader extends ClassLoader implements GeneratedClassLoader{
public BaseAndroidClassLoader(ClassLoader parent){
super(parent);
}
@Override
public Class<?> defineClass(String name, byte[] data){
try{
DexOptions dexOptions = new DexOptions();
DexFile dexFile = new DexFile(dexOptions);
DirectClassFile classFile = new DirectClassFile(data, name.replace('.', '/') + ".class", true);
classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
classFile.getMagic();
DxContext context = new DxContext();
dexFile.add(CfTranslator.translate(context, classFile, null, new CfOptions(), dexOptions, dexFile));
Dex dex = new Dex(dexFile.toDex(null, false));
Dex oldDex = getLastDex();
if(oldDex != null){
dex = new DexMerger(new Dex[]{dex, oldDex}, CollisionPolicy.KEEP_FIRST, context).merge();
}
return loadClass(dex, name);
}catch(IOException | ClassNotFoundException e){
throw new FatalLoadingException(e);
}
}
protected abstract Class<?> loadClass(Dex dex, String name) throws ClassNotFoundException;
protected abstract Dex getLastDex();
protected abstract void reset();
@Override
public void linkClass(Class<?> aClass){}
@Override
public Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException{
Class<?> loadedClass = findLoadedClass(name);
if(loadedClass == null){
Dex dex = getLastDex();
if(dex != null){
loadedClass = loadClass(dex, name);
}
if(loadedClass == null){
loadedClass = getParent().loadClass(name);
}
}
return loadedClass;
}
/** Might be thrown in any Rhino method that loads bytecode if the loading failed. */
public static class FatalLoadingException extends RuntimeException{
FatalLoadingException(Throwable t){
super("Failed to define class", t);
}
}
static class FileAndroidClassLoader extends BaseAndroidClassLoader{
private static int instanceCounter = 0;
private final File dexFile;
public FileAndroidClassLoader(ClassLoader parent, File cacheDir){
super(parent);
int id = instanceCounter++;
dexFile = new File(cacheDir, id + ".dex");
cacheDir.mkdirs();
reset();
}
@Override
protected Class<?> loadClass(@NonNull Dex dex, @NonNull String name) throws ClassNotFoundException{
try{
dex.writeTo(dexFile);
}catch(IOException e){
e.printStackTrace();
}
return new DexClassLoader(dexFile.getPath(), ((AndroidApplication)Core.app).getContext().getCacheDir().getAbsolutePath(), null, getParent()).loadClass(name);
}
@Nullable
@Override
protected Dex getLastDex(){
if(dexFile.exists()){
try{
return new Dex(dexFile);
}catch(IOException e){
e.printStackTrace();
}
}
return null;
}
@Override
protected void reset(){
dexFile.delete();
}
}
@TargetApi(Build.VERSION_CODES.O)
static class InMemoryAndroidClassLoader extends BaseAndroidClassLoader{
@Nullable private Dex last;
public InMemoryAndroidClassLoader(ClassLoader parent){
super(parent);
}
@Override
protected Class<?> loadClass(@NonNull Dex dex, @NonNull String name) throws ClassNotFoundException{
last = dex;
return new InMemoryDexClassLoader(ByteBuffer.wrap(dex.getBytes()), getParent()).loadClass(name);
}
@Nullable
@Override
protected Dex getLastDex(){
return last;
}
@Override
protected void reset(){
last = null;
}
}
}
}

View File

@ -1,44 +0,0 @@
package io.anuke.mindustry.rhino;
import android.os.*;
import org.mozilla.javascript.*;
import java.io.*;
/**
* Ensures that the classLoader used is correct
* @author F43nd1r
* @since 11.01.2016
*/
public class AndroidContextFactory extends ContextFactory{
private final File cacheDirectory;
/**
* Create a new factory. It will cache generated code in the given directory
* @param cacheDirectory the cache directory
*/
public AndroidContextFactory(File cacheDirectory){
this.cacheDirectory = cacheDirectory;
initApplicationClassLoader(createClassLoader(AndroidContextFactory.class.getClassLoader()));
}
/**
* Create a ClassLoader which is able to deal with bytecode
* @param parent the parent of the create classloader
* @return a new ClassLoader
*/
@Override
public BaseAndroidClassLoader createClassLoader(ClassLoader parent){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
return new InMemoryAndroidClassLoader(parent);
}
return new FileAndroidClassLoader(parent, cacheDirectory);
}
@Override
protected void onContextReleased(final Context cx){
super.onContextReleased(cx);
((BaseAndroidClassLoader)cx.getApplicationClassLoader()).reset();
}
}

View File

@ -1,99 +0,0 @@
package io.anuke.mindustry.rhino;
import com.android.dex.*;
import com.android.dx.cf.direct.*;
import com.android.dx.command.dexer.*;
import com.android.dx.dex.*;
import com.android.dx.dex.cf.*;
import com.android.dx.dex.file.*;
import com.android.dx.merge.*;
import org.mozilla.javascript.*;
import java.io.*;
/**
* Compiles java bytecode to dex bytecode and loads it
* @author F43nd1r
* @since 11.01.2016
*/
abstract class BaseAndroidClassLoader extends ClassLoader implements GeneratedClassLoader{
/**
* Create a new instance with the given parent classloader
* @param parent the parent
*/
public BaseAndroidClassLoader(ClassLoader parent){
super(parent);
}
/**
* {@inheritDoc}
*/
@Override
public Class<?> defineClass(String name, byte[] data){
try{
DexOptions dexOptions = new DexOptions();
DexFile dexFile = new DexFile(dexOptions);
DirectClassFile classFile = new DirectClassFile(data, name.replace('.', '/') + ".class", true);
classFile.setAttributeFactory(StdAttributeFactory.THE_ONE);
classFile.getMagic();
DxContext context = new DxContext();
dexFile.add(CfTranslator.translate(context, classFile, null, new CfOptions(), dexOptions, dexFile));
Dex dex = new Dex(dexFile.toDex(null, false));
Dex oldDex = getLastDex();
if(oldDex != null){
dex = new DexMerger(new Dex[]{dex, oldDex}, CollisionPolicy.KEEP_FIRST, context).merge();
}
return loadClass(dex, name);
}catch(IOException | ClassNotFoundException e){
throw new FatalLoadingException(e);
}
}
protected abstract Class<?> loadClass(Dex dex, String name) throws ClassNotFoundException;
protected abstract Dex getLastDex();
protected abstract void reset();
/**
* Does nothing
* @param aClass ignored
*/
@Override
public void linkClass(Class<?> aClass){
//doesn't make sense on android
}
/**
* Try to load a class. This will search all defined classes, all loaded jars and the parent class loader.
* @param name the name of the class to load
* @param resolve ignored
* @return the class
* @throws ClassNotFoundException if the class could not be found in any of the locations
*/
@Override
public Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException{
Class<?> loadedClass = findLoadedClass(name);
if(loadedClass == null){
Dex dex = getLastDex();
if(dex != null){
loadedClass = loadClass(dex, name);
}
if(loadedClass == null){
loadedClass = getParent().loadClass(name);
}
}
return loadedClass;
}
/**
* Might be thrown in any Rhino method that loads bytecode if the loading failed
*/
public static class FatalLoadingException extends RuntimeException{
FatalLoadingException(Throwable t){
super("Failed to define class", t);
}
}
}

View File

@ -1,59 +0,0 @@
package io.anuke.mindustry.rhino;
import com.android.dex.*;
import dalvik.system.*;
import io.anuke.arc.*;
import io.anuke.arc.backends.android.surfaceview.*;
import io.anuke.arc.util.ArcAnnotate.*;
import java.io.*;
/**
* @author F43nd1r
* @since 24.10.2017
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
class FileAndroidClassLoader extends BaseAndroidClassLoader{
private static int instanceCounter = 0;
private final File dexFile;
/**
* Create a new instance with the given parent classloader
* @param parent the parent
*/
public FileAndroidClassLoader(ClassLoader parent, File cacheDir){
super(parent);
int id = instanceCounter++;
dexFile = new File(cacheDir, id + ".dex");
cacheDir.mkdirs();
reset();
}
@Override
protected Class<?> loadClass(@NonNull Dex dex, @NonNull String name) throws ClassNotFoundException{
try{
dex.writeTo(dexFile);
}catch(IOException e){
e.printStackTrace();
}
return new DexClassLoader(dexFile.getPath(), ((AndroidApplication)Core.app).getContext().getCacheDir().getAbsolutePath(), null, getParent()).loadClass(name);
}
@Nullable
@Override
protected Dex getLastDex(){
if(dexFile.exists()){
try{
return new Dex(dexFile);
}catch(IOException e){
e.printStackTrace();
}
}
return null;
}
@Override
protected void reset(){
dexFile.delete();
}
}

View File

@ -1,42 +0,0 @@
package io.anuke.mindustry.rhino;
import android.annotation.*;
import android.os.*;
import com.android.dex.*;
import dalvik.system.*;
import io.anuke.arc.util.ArcAnnotate.NonNull;
import io.anuke.arc.util.ArcAnnotate.Nullable;
import java.nio.*;
/**
* @author F43nd1r
* @since 24.10.2017
*/
@TargetApi(Build.VERSION_CODES.O)
class InMemoryAndroidClassLoader extends BaseAndroidClassLoader{
@Nullable
private Dex last;
public InMemoryAndroidClassLoader(ClassLoader parent){
super(parent);
}
@Override
protected Class<?> loadClass(@NonNull Dex dex, @NonNull String name) throws ClassNotFoundException{
last = dex;
return new InMemoryDexClassLoader(ByteBuffer.wrap(dex.getBytes()), getParent()).loadClass(name);
}
@Nullable
@Override
protected Dex getLastDex(){
return last;
}
@Override
protected void reset(){
last = null;
}
}

View File

@ -1,83 +0,0 @@
package io.anuke.mindustry.rhino;
import org.mozilla.javascript.*;
import java.io.*;
/**
* Helps to prepare a Rhino Context for usage on android.
* @author F43nd1r
* @since 11.01.2016
*/
public class RhinoBuilder{
private final File cacheDirectory;
/**
* Constructs a new helper using the default temporary directory.
* Note: It is recommended to use a custom directory, so no permission problems occur.
*/
public RhinoBuilder(){
this(new File(System.getProperty("java.io.tmpdir", "."), "classes"));
}
/**
* Constructs a new helper using a directory in the applications cache.
* @param context any context
*/
public RhinoBuilder(android.content.Context context){
this(new File(context.getCacheDir(), "classes"));
}
/**
* Constructs a helper using the specified directory as cache.
* @param cacheDirectory the cache directory to use
*/
public RhinoBuilder(File cacheDirectory){
this.cacheDirectory = cacheDirectory;
}
/**
* call this instead of {@link Context#enter()}
* @return a context prepared for android
*/
public Context enterContext(){
if(!SecurityController.hasGlobal())
SecurityController.initGlobal(new SecurityController(){
@Override
public GeneratedClassLoader createClassLoader(ClassLoader classLoader, Object o){
return Context.getCurrentContext().createClassLoader(classLoader);
}
@Override
public Object getDynamicSecurityDomain(Object o){
return null;
}
});
return getContextFactory().enterContext();
}
/**
* @return The Context factory which has to be used on android.
*/
public AndroidContextFactory getContextFactory(){
AndroidContextFactory factory;
if(!ContextFactory.hasExplicitGlobal()){
factory = new AndroidContextFactory(cacheDirectory);
ContextFactory.getGlobalSetter().setContextFactoryGlobal(factory);
}else if(!(ContextFactory.getGlobal() instanceof AndroidContextFactory)){
throw new IllegalStateException("Cannot initialize factory for Android Rhino: There is already another factory");
}else{
factory = (AndroidContextFactory)ContextFactory.getGlobal();
}
return factory;
}
/**
* @return a context prepared for android
* @deprecated use {@link #enterContext()} instead
*/
@Deprecated
public static Context prepareContext(){
return new RhinoBuilder().enterContext();
}
}

View File

@ -1,3 +1,3 @@
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=b38d5e9c24d921f87f33bc48fcb65e245c1b1d0c
archash=190918590e8401b1686ecb9167e3c2a9e77eafaa