mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-03-11 18:39:16 +07:00
More cleanup
This commit is contained in:
parent
0078a8cb8e
commit
2a03ef0c43
@ -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
|
||||
|
229
android/src/io/anuke/mindustry/AndroidRhinoContext.java
Normal file
229
android/src/io/anuke/mindustry/AndroidRhinoContext.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
org.gradle.daemon=true
|
||||
org.gradle.jvmargs=-Xms256m -Xmx1024m
|
||||
archash=b38d5e9c24d921f87f33bc48fcb65e245c1b1d0c
|
||||
archash=190918590e8401b1686ecb9167e3c2a9e77eafaa
|
||||
|
Loading…
Reference in New Issue
Block a user