Skip to content

Commit 162be8d

Browse files
committedJan 19, 2015
OC integration (semi-working, leaking API)
1 parent 2f13cef commit 162be8d

32 files changed

+691
-49
lines changed
 

‎build.gradle

+12
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,18 @@ task deobfJar(type: Jar) {
155155
}
156156
}
157157

158+
task deobfSourcesJar(type: Jar, dependsOn: classes) {
159+
classifier = 'deobf-src'
160+
from sourceSets.main.allSource
161+
}
162+
163+
task sourcesJar(type: Jar, dependsOn: classes) {
164+
classifier = 'src'
165+
from sourceSets.main.java
166+
}
167+
158168
artifacts {
159169
archives deobfJar
170+
archives sourcesJar
171+
archives deobfSourcesJar
160172
}

‎src/main/java/openperipheral/CommandDump.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import openmods.OpenMods;
1313
import openperipheral.adapter.AdapterRegistry;
1414
import openperipheral.adapter.IMethodExecutor;
15-
import openperipheral.converter.wrappers.AdapterWrapper;
15+
import openperipheral.adapter.wrappers.AdapterWrapper;
1616
import openperipheral.interfaces.cc.Registries;
1717
import openperipheral.util.DocBuilder;
1818

‎src/main/java/openperipheral/OpenPeripheralCore.java

+8
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
import java.io.File;
44

5+
import li.cil.oc.api.Driver;
56
import net.minecraftforge.client.ClientCommandHandler;
67
import net.minecraftforge.common.MinecraftForge;
78
import net.minecraftforge.common.config.Configuration;
89
import openmods.config.properties.ConfigProcessing;
910
import openperipheral.adapter.TileEntityBlacklist;
11+
import openperipheral.api.IOpenPeripheral;
1012
import openperipheral.interfaces.cc.Registries;
1113
import openperipheral.interfaces.cc.providers.PeripheralProvider;
14+
import openperipheral.interfaces.oc.providers.DriverOpenPeripheral;
1215
import cpw.mods.fml.common.Mod;
1316
import cpw.mods.fml.common.Mod.EventHandler;
1417
import cpw.mods.fml.common.event.*;
@@ -31,18 +34,23 @@ public void preInit(FMLPreInitializationEvent evt) {
3134
if (config.hasChanged()) config.save();
3235

3336
MinecraftForge.EVENT_BUS.register(TileEntityBlacklist.INSTANCE);
37+
38+
FMLInterModComms.sendMessage("OpenComputers", "blacklistPeripheral", IOpenPeripheral.class.getName());
3439
}
3540

3641
@Mod.EventHandler
3742
public void init(FMLInitializationEvent evt) {
3843
ClientCommandHandler.instance.registerCommand(new CommandDump());
44+
45+
Driver.add(new DriverOpenPeripheral());
3946
}
4047

4148
// this method should be called as late as possible, to make sure we are last on provider list
4249
@Mod.EventHandler
4350
public void loadComplete(FMLLoadCompleteEvent evt) {
4451
Registries.OBJECT_VALIDATOR.validate();
4552
Registries.PERIPHERAL_VALIDATOR.validate();
53+
4654
ComputerCraftAPI.registerPeripheralProvider(new PeripheralProvider());
4755
}
4856

‎src/main/java/openperipheral/TypeConversionRegistry.java

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.List;
55
import java.util.Set;
66

7+
import li.cil.oc.api.machine.Value;
78
import openmods.Log;
89
import openperipheral.api.ITypeConverter;
910
import openperipheral.api.ITypeConvertersRegistry;
@@ -43,6 +44,7 @@ private boolean isIgnored(Class<?> cls) {
4344

4445
private TypeConversionRegistry() {
4546
registerIgnored(ILuaObject.class, true);
47+
registerIgnored(Value.class, true);
4648

4749
converters.add(new ConverterGameProfile());
4850
converters.add(new ConverterFluidTankInfo());

‎src/main/java/openperipheral/adapter/AdapterRegistry.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66

77
import net.minecraft.tileentity.TileEntity;
88
import openmods.Log;
9+
import openperipheral.adapter.wrappers.AdapterWrapper;
10+
import openperipheral.adapter.wrappers.ExternalAdapterWrapper;
11+
import openperipheral.adapter.wrappers.InlineAdapterWrapper;
912
import openperipheral.api.*;
10-
import openperipheral.converter.wrappers.AdapterWrapper;
11-
import openperipheral.converter.wrappers.ExternalAdapterWrapper;
12-
import openperipheral.converter.wrappers.InlineAdapterWrapper;
1313

1414
import com.google.common.base.Preconditions;
1515
import com.google.common.collect.HashMultimap;

‎src/main/java/openperipheral/adapter/DefaultArgNames.java

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
public class DefaultArgNames {
44
public static final String ARG_CONTEXT = "context";
55
public static final String ARG_COMPUTER = "computer";
6+
public static final String ARG_TARGET = "target";
67
}

‎src/main/java/openperipheral/adapter/PropertyListBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public IMethodCall setOptionalArg(String name, Object value) {
7171
}
7272

7373
@Override
74-
public Object[] call(Object[] args) throws Exception {
74+
public Object[] call(Object[] args) {
7575
return context.call(target, args);
7676
}
7777
};

‎src/main/java/openperipheral/adapter/WrappedEntityBase.java

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
package openperipheral.adapter;
22

3-
import java.util.List;
43
import java.util.Map;
54

6-
import com.google.common.collect.ImmutableList;
7-
import com.google.common.collect.Lists;
8-
95
public class WrappedEntityBase {
106

117
protected final String[] names;
12-
protected final List<IMethodExecutor> methods;
13-
14-
public WrappedEntityBase(Map<String, IMethodExecutor> list) {
15-
List<String> names = Lists.newArrayList();
16-
ImmutableList.Builder<IMethodExecutor> methods = ImmutableList.builder();
17-
18-
for (Map.Entry<String, IMethodExecutor> e : list.entrySet()) {
19-
names.add(e.getKey());
20-
methods.add(e.getValue());
8+
protected final IMethodExecutor[] methods;
9+
10+
public WrappedEntityBase(Map<String, IMethodExecutor> methods) {
11+
final int methodCount = methods.size();
12+
this.names = new String[methodCount];
13+
this.methods = new IMethodExecutor[methodCount];
14+
15+
int i = 0;
16+
for (Map.Entry<String, IMethodExecutor> e : methods.entrySet()) {
17+
this.names[i] = e.getKey();
18+
this.methods[i] = e.getValue();
19+
i++;
2120
}
21+
}
2222

23-
this.methods = methods.build();
24-
this.names = names.toArray(new String[0]);
23+
public IMethodExecutor[] getMethods() {
24+
return methods;
2525
}
2626

2727
public String[] getMethodNames() {
2828
return names;
2929
}
3030

3131
public IMethodExecutor getMethod(int index) {
32-
return methods.get(index);
32+
return methods[index];
3333
}
3434

3535
public String getMethodName(int index) {

‎src/main/java/openperipheral/adapter/composed/ClassMethodsListBuilder.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import openperipheral.adapter.AdapterRegistry;
99
import openperipheral.adapter.IDescriptable;
1010
import openperipheral.adapter.IMethodExecutor;
11-
import openperipheral.converter.wrappers.AdapterWrapper;
12-
import openperipheral.converter.wrappers.TechnicalAdapterWrapper;
11+
import openperipheral.adapter.wrappers.AdapterWrapper;
12+
import openperipheral.adapter.wrappers.TechnicalAdapterWrapper;
1313

1414
import com.google.common.collect.ImmutableMap;
1515
import com.google.common.collect.Maps;

‎src/main/java/openperipheral/adapter/composed/MethodsListerHelper.java

+14-9
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66

77
import openperipheral.adapter.IDescriptable;
88
import openperipheral.adapter.IMethodExecutor;
9-
import openperipheral.api.Asynchronous;
10-
import openperipheral.api.LuaCallable;
11-
import openperipheral.api.LuaReturnType;
9+
import openperipheral.api.*;
1210

1311
import com.google.common.base.Joiner;
12+
import com.google.common.base.Preconditions;
1413
import com.google.common.collect.Lists;
1514
import com.google.common.collect.Maps;
1615

@@ -44,12 +43,18 @@ public Map<String, Boolean> listSources() {
4443
}
4544

4645
@LuaCallable(returnTypes = LuaReturnType.TABLE, description = "Get a complete table of information about all available methods")
47-
public Map<?, ?> getAdvancedMethodsData() {
48-
Map<String, Object> info = Maps.newHashMap();
49-
for (Map.Entry<String, IMethodExecutor> e : methods.entrySet()) {
50-
final IDescriptable m = e.getValue().description();
51-
info.put(e.getKey(), m.describe());
46+
public Map<?, ?> getAdvancedMethodsData(@Optionals @Arg(name = "method") String methodName) {
47+
if (methodName != null) {
48+
IMethodExecutor method = methods.get(methodName);
49+
Preconditions.checkArgument(method != null, "Method not found");
50+
return method.description().describe();
51+
} else {
52+
Map<String, Object> info = Maps.newHashMap();
53+
for (Map.Entry<String, IMethodExecutor> e : methods.entrySet()) {
54+
final IDescriptable m = e.getValue().description();
55+
info.put(e.getKey(), m.describe());
56+
}
57+
return info;
5258
}
53-
return info;
5459
}
5560
}

‎src/main/java/openperipheral/adapter/method/Argument.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@ protected Class<?> getArgType(Class<?> javaArgClass) {
3030
}
3131

3232
public Object convert(Iterator<Object> args) {
33-
Preconditions.checkState(args.hasNext(), "Not enough arguments, first missing: %s", name);
33+
Preconditions.checkArgument(args.hasNext(), "Not enough arguments, first missing: %s", name);
3434
Object arg = args.next();
35-
Preconditions.checkNotNull(arg, "Argument %s cannot be null", name);
35+
Preconditions.checkArgument(arg != null, "Argument %s cannot be null", name);
3636
return convertSingleArg(arg);
3737
}
3838

3939
protected final Object convertSingleArg(Object o) {
4040
if (o == null) return null;
4141
Object converted = TypeConversionRegistry.INSTANCE.fromLua(o, javaType);
42-
Preconditions.checkNotNull(converted, "Failed to convert arg '%s' value '%s' to '%s'", name, o, javaType.getSimpleName());
42+
Preconditions.checkArgument(converted != null, "Failed to convert arg '%s' value '%s' to '%s'", name, o, javaType.getSimpleName());
4343
return converted;
4444
}
4545

‎src/main/java/openperipheral/adapter/method/MethodDeclaration.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,13 @@ private CallWrap setCallArgs(Object[] luaValues) {
287287
setArg(arg.javaArgIndex, value);
288288
}
289289

290-
Preconditions.checkState(!it.hasNext(), "Too many arguments!");
290+
Preconditions.checkArgument(!it.hasNext(), "Too many arguments!");
291291
} catch (ArrayIndexOutOfBoundsException e) {
292292
Log.log(Level.TRACE, e, "Trying to access arg index, args = %s", Arrays.toString(luaValues));
293293
throw new IllegalArgumentException(String.format("Invalid Lua parameter count, needs %s, got %s", luaArgs.size(), luaValues.length));
294294
}
295+
} catch (IllegalArgumentException e) {
296+
throw e;
295297
} catch (Exception e) {
296298
throw new AdapterLogicException(e);
297299
}

‎src/main/java/openperipheral/adapter/method/NullableArgument.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protected Class<?> getArgType(Class<?> javaArgClass) {
2121

2222
@Override
2323
public Object convert(Iterator<Object> args) {
24-
Preconditions.checkState(args.hasNext(), "Not enough arguments, first missing: %s", name);
24+
Preconditions.checkArgument(args.hasNext(), "Not enough arguments, first missing: %s", name);
2525
Object arg = args.next();
2626
return convertSingleArg(arg);
2727
}

‎src/main/java/openperipheral/adapter/method/VarArgument.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ protected Class<?> getArgType(Class<?> javaArgClass) {
2424
}
2525

2626
protected void checkArgument(Object value) {
27-
Preconditions.checkNotNull(value, "Vararg parameter '%s' has null value, but is not marked as nullable", name);
27+
Preconditions.checkArgument(value != null, "Vararg parameter '%s' has null value, but is not marked as nullable", name);
2828
}
2929

3030
@Override

‎src/main/java/openperipheral/converter/wrappers/AdapterWrapper.java ‎src/main/java/openperipheral/adapter/wrappers/AdapterWrapper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package openperipheral.converter.wrappers;
1+
package openperipheral.adapter.wrappers;
22

33
import java.lang.reflect.Method;
44
import java.util.List;

‎src/main/java/openperipheral/converter/wrappers/ExternalAdapterWrapper.java ‎src/main/java/openperipheral/adapter/wrappers/ExternalAdapterWrapper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package openperipheral.converter.wrappers;
1+
package openperipheral.adapter.wrappers;
22

33
import java.lang.reflect.Method;
44

‎src/main/java/openperipheral/converter/wrappers/InlineAdapterWrapper.java ‎src/main/java/openperipheral/adapter/wrappers/InlineAdapterWrapper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package openperipheral.converter.wrappers;
1+
package openperipheral.adapter.wrappers;
22

33
import java.lang.reflect.Method;
44
import java.util.List;

‎src/main/java/openperipheral/converter/wrappers/MethodExecutorBase.java ‎src/main/java/openperipheral/adapter/wrappers/MethodExecutorBase.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package openperipheral.converter.wrappers;
1+
package openperipheral.adapter.wrappers;
22

33
import java.util.Map;
44

‎src/main/java/openperipheral/converter/wrappers/TechnicalAdapterWrapper.java ‎src/main/java/openperipheral/adapter/wrappers/TechnicalAdapterWrapper.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
package openperipheral.converter.wrappers;
1+
package openperipheral.adapter.wrappers;
22

33
import java.lang.reflect.Method;
44

5+
import openperipheral.adapter.DefaultArgNames;
56
import openperipheral.adapter.IMethodCall;
67
import openperipheral.adapter.IMethodExecutor;
78
import openperipheral.adapter.method.MethodDeclaration;
@@ -33,7 +34,7 @@ public IMethodExecutor createExecutor(Method method, MethodDeclaration decl) {
3334
return new MethodExecutorBase(decl, asyncChecker.isAsync(method)) {
3435
@Override
3536
public IMethodCall startCall(Object target) {
36-
return method.startCall(adapter);
37+
return method.startCall(adapter).setOptionalArg(DefaultArgNames.ARG_TARGET, target);
3738
}
3839
};
3940
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package openperipheral.api;
2+
3+
/**
4+
* Marker interface for all generated peripherals, can be used for blacklisting
5+
*/
6+
public interface IOpenPeripheral {}

‎src/main/java/openperipheral/api/LuaCallable.java

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.lang.annotation.*;
44

5+
import li.cil.repack.com.naef.jnlua.LuaType;
6+
57
/**
68
* Used to mark methods that should be visible in Lua.
79
*

‎src/main/java/openperipheral/interfaces/cc/providers/PeripheralProvider.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@
1111
import openmods.reflection.ReflectionHelper;
1212
import openperipheral.adapter.IMethodExecutor;
1313
import openperipheral.adapter.TileEntityBlacklist;
14-
import openperipheral.api.ExposeInterface;
15-
import openperipheral.api.ICustomPeripheralProvider;
16-
import openperipheral.api.Volatile;
14+
import openperipheral.api.*;
1715
import openperipheral.interfaces.cc.Registries;
1816
import openperipheral.interfaces.cc.wrappers.AdapterPeripheral;
1917
import openperipheral.interfaces.cc.wrappers.ProxyAdapterPeripheral;
@@ -127,6 +125,7 @@ public static IPeripheral createAdaptedPeripheral(Object target) {
127125

128126
Set<Class<?>> allImplemented = Sets.newHashSet(proxied);
129127
allImplemented.add(IPeripheral.class);
128+
allImplemented.add(IOpenPeripheral.class);
130129

131130
InvocationHandler handler = new ProxyAdapterPeripheral(methods, target);
132131

‎src/main/java/openperipheral/interfaces/cc/wrappers/AdapterPeripheral.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import openperipheral.adapter.IMethodExecutor;
1111
import openperipheral.adapter.WrappedEntityBase;
1212
import openperipheral.api.IAttachable;
13+
import openperipheral.api.IOpenPeripheral;
1314
import openperipheral.api.IWorldProvider;
1415
import openperipheral.interfaces.cc.executors.*;
1516
import openperipheral.interfaces.cc.executors.SynchronousExecutor.TileEntityExecutor;
@@ -28,7 +29,7 @@
2829
import dan200.computercraft.api.peripheral.IComputerAccess;
2930
import dan200.computercraft.api.peripheral.IPeripheral;
3031

31-
public class AdapterPeripheral extends WrappedEntityBase implements IPeripheral {
32+
public class AdapterPeripheral extends WrappedEntityBase implements IPeripheral, IOpenPeripheral {
3233

3334
private static final String MOUNT_NAME = "openp";
3435
private static final IMount MOUNT = new ResourceMount();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package openperipheral.interfaces.oc;
2+
3+
import li.cil.oc.api.machine.Context;
4+
import openperipheral.adapter.*;
5+
6+
public class Registries {
7+
8+
public static final MethodsValidator PERIPHERAL_VALIDATOR = new MethodsValidator(AdapterRegistry.PERIPHERAL_ADAPTERS);
9+
10+
static {
11+
PERIPHERAL_VALIDATOR.addArg(DefaultArgNames.ARG_CONTEXT, Context.class);
12+
}
13+
14+
public static final ComposedMethodsFactory PERIPHERAL_METHODS_FACTORY = new ComposedMethodsFactory(AdapterRegistry.PERIPHERAL_ADAPTERS);
15+
16+
public static final MethodsValidator OBJECT_VALIDATOR = new MethodsValidator(AdapterRegistry.OBJECT_ADAPTERS);
17+
18+
static {
19+
OBJECT_VALIDATOR.addArg(DefaultArgNames.ARG_CONTEXT, Context.class);
20+
}
21+
22+
public static final ComposedMethodsFactory OBJECT_METHODS_FACTORY = new ComposedMethodsFactory(AdapterRegistry.OBJECT_ADAPTERS);
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package openperipheral.interfaces.oc.asm;
2+
3+
import li.cil.oc.api.driver.NamedBlock;
4+
import li.cil.oc.api.machine.Arguments;
5+
import li.cil.oc.api.machine.Context;
6+
import li.cil.oc.api.prefab.ManagedEnvironment;
7+
import openperipheral.adapter.DefaultArgNames;
8+
import openperipheral.adapter.IMethodExecutor;
9+
import openperipheral.util.PeripheralUtils;
10+
11+
/**
12+
* Warning: don't change method names or arguments, used in {@link EnvironmentCodeGenerator}
13+
*
14+
* @author boq
15+
*
16+
*/
17+
public abstract class EnvironmentBase extends ManagedEnvironment implements NamedBlock {
18+
19+
public static final String METHODS_FIELD = "methods";
20+
21+
private final String type;
22+
23+
public EnvironmentBase(Object target) {
24+
this.type = PeripheralUtils.getNameForTarget(target);
25+
}
26+
27+
protected static Object[] call(Object target, IMethodExecutor executor, Context context, Arguments arguments) throws Exception {
28+
Object[] args = arguments.toArray();
29+
return executor.startCall(target).setOptionalArg(DefaultArgNames.ARG_CONTEXT, context).call(args);
30+
}
31+
32+
@Override
33+
public String preferredName() {
34+
return type;
35+
}
36+
37+
@Override
38+
public int priority() {
39+
return -1; // TODO: DriverPeripheral is at 0, but we can blacklist
40+
}
41+
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package openperipheral.interfaces.oc.asm;
2+
3+
import java.util.Collection;
4+
import java.util.Map;
5+
import java.util.Set;
6+
7+
import li.cil.oc.api.machine.Arguments;
8+
import li.cil.oc.api.machine.Callback;
9+
import li.cil.oc.api.machine.Context;
10+
import openperipheral.adapter.IMethodExecutor;
11+
import openperipheral.adapter.WrappedEntityBase;
12+
13+
import org.objectweb.asm.*;
14+
import org.objectweb.asm.commons.Method;
15+
16+
import com.google.common.collect.Maps;
17+
18+
public class EnvironmentCodeGenerator {
19+
20+
private static final String TARGET_FIELD = "target";
21+
22+
private static final Type BASE_TYPE = Type.getType(EnvironmentBase.class);
23+
24+
public static final Type EXECUTOR_TYPE = Type.getType(IMethodExecutor.class);
25+
26+
public static final Type EXECUTORS_TYPE = Type.getType(IMethodExecutor[].class);
27+
28+
private static final Type OBJECT_TYPE = Type.getType(Object.class);
29+
30+
private static final Type OBJECTS_TYPE = Type.getType(Object[].class);
31+
32+
private static final Type CONTEXT_TYPE = Type.getType(Context.class);
33+
34+
private static final Type ARGUMENTS_TYPE = Type.getType(Arguments.class);
35+
36+
private static final Type CALLBACK_TYPE = Type.getType(Callback.class);
37+
38+
public static final Type SUPER_CTOR_TYPE = Type.getMethodType(Type.VOID_TYPE, OBJECT_TYPE);
39+
40+
public static final Type GET_METHOD_TYPE = Type.getMethodType(EXECUTOR_TYPE, Type.INT_TYPE);
41+
42+
public static final Type CALL_TYPE = Type.getMethodType(OBJECTS_TYPE, OBJECT_TYPE, EXECUTOR_TYPE, CONTEXT_TYPE, ARGUMENTS_TYPE);
43+
44+
public static final Type WRAP_TYPE = Type.getMethodType(OBJECTS_TYPE, CONTEXT_TYPE, ARGUMENTS_TYPE);
45+
46+
private static void visitIntConst(MethodVisitor mv, int value) {
47+
switch (value) {
48+
case 0:
49+
case 1:
50+
case 2:
51+
case 3:
52+
case 4:
53+
case 5:
54+
mv.visitInsn(Opcodes.ICONST_0 + value);
55+
break;
56+
default:
57+
mv.visitLdcInsn(value);
58+
break;
59+
}
60+
}
61+
62+
@SuppressWarnings("deprecation")
63+
public byte[] generate(String clsName, Class<?> targetClass, Set<Class<?>> exposedInterfaces, WrappedEntityBase methods) {
64+
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
65+
66+
writer.visit(Opcodes.V1_6,
67+
Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_SUPER,
68+
clsName, null, BASE_TYPE.getInternalName(), getInterfaces(exposedInterfaces));
69+
70+
Type targetType = Type.getType(targetClass);
71+
72+
writer.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, TARGET_FIELD, targetType.getDescriptor(), null, null);
73+
writer.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, EnvironmentBase.METHODS_FIELD, EXECUTORS_TYPE.getDescriptor(), null, null);
74+
75+
{
76+
final Type ctorType = Type.getMethodType(Type.VOID_TYPE, targetType);
77+
78+
MethodVisitor init = writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, "<init>", ctorType.getDescriptor(), null, null);
79+
init.visitCode();
80+
init.visitVarInsn(Opcodes.ALOAD, 0);
81+
init.visitVarInsn(Opcodes.ALOAD, 1);
82+
init.visitInsn(Opcodes.DUP2);
83+
init.visitMethodInsn(Opcodes.INVOKESPECIAL, BASE_TYPE.getInternalName(), "<init>", SUPER_CTOR_TYPE.getDescriptor());
84+
init.visitFieldInsn(Opcodes.PUTFIELD, clsName, TARGET_FIELD, targetType.getDescriptor());
85+
init.visitInsn(Opcodes.RETURN);
86+
87+
init.visitMaxs(0, 0);
88+
89+
init.visitEnd();
90+
}
91+
92+
final Map<Method, Type> exposedMethods = getExposedMethods(exposedInterfaces);
93+
for (Map.Entry<Method, Type> e : exposedMethods.entrySet()) {
94+
final Method method = e.getKey();
95+
final Type intf = e.getValue();
96+
MethodVisitor mv = writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, method.getName(), method.getDescriptor(), null, null);
97+
98+
mv.visitCode();
99+
100+
mv.visitVarInsn(Opcodes.ALOAD, 0);
101+
mv.visitFieldInsn(Opcodes.GETFIELD, clsName, TARGET_FIELD, targetType.getDescriptor());
102+
103+
Type[] args = method.getArgumentTypes();
104+
for (int i = 0; i < args.length; i++)
105+
mv.visitVarInsn(args[i].getOpcode(Opcodes.ILOAD), i + 1);
106+
107+
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, intf.getInternalName(), method.getName(), method.getDescriptor());
108+
Type returnType = method.getReturnType();
109+
mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
110+
111+
mv.visitMaxs(0, 0);
112+
mv.visitEnd();
113+
}
114+
115+
String[] names = methods.getMethodNames();
116+
117+
for (int i = 0; i < names.length; i++) {
118+
IMethodExecutor executor = methods.getMethod(i);
119+
120+
String name = names[i];
121+
String methodName = name.replaceAll("[^A-Za-z0-9_]", "_") + "$" + Integer.toString(i);
122+
123+
MethodVisitor wrap = writer.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, methodName, WRAP_TYPE.getDescriptor(), null, null);
124+
125+
AnnotationVisitor av = wrap.visitAnnotation(CALLBACK_TYPE.getDescriptor(), true);
126+
av.visit("value", name);
127+
av.visit("direct", executor.isAsynchronous());
128+
av.visit("doc", executor.description().signature()); // TODO: convert to expected format
129+
av.visitEnd();
130+
// TODO: getter/setter
131+
132+
av.visitEnd();
133+
134+
wrap.visitVarInsn(Opcodes.ALOAD, 0); // this
135+
wrap.visitFieldInsn(Opcodes.GETFIELD, clsName, TARGET_FIELD, targetType.getDescriptor());
136+
137+
wrap.visitFieldInsn(Opcodes.GETSTATIC, clsName, EnvironmentBase.METHODS_FIELD, EXECUTORS_TYPE.getDescriptor());
138+
visitIntConst(wrap, i);
139+
wrap.visitInsn(Opcodes.AALOAD); // executor
140+
141+
wrap.visitVarInsn(Opcodes.ALOAD, 1); // context
142+
wrap.visitVarInsn(Opcodes.ALOAD, 2); // args
143+
wrap.visitMethodInsn(Opcodes.INVOKESTATIC, BASE_TYPE.getInternalName(), "call", CALL_TYPE.getDescriptor());
144+
wrap.visitInsn(Opcodes.ARETURN);
145+
146+
wrap.visitMaxs(0, 0);
147+
148+
wrap.visitEnd();
149+
}
150+
151+
writer.visitEnd();
152+
153+
return writer.toByteArray();
154+
}
155+
156+
private static String[] getInterfaces(Set<Class<?>> exposedInterfaces) {
157+
String[] result = new String[exposedInterfaces.size()];
158+
int i = 0;
159+
for (Class<?> cls : exposedInterfaces)
160+
result[i++] = Type.getInternalName(cls);
161+
162+
return result;
163+
}
164+
165+
private static Map<Method, Type> getExposedMethods(Collection<Class<?>> exposedInterfaces) {
166+
Map<Method, Type> result = Maps.newHashMap();
167+
168+
for (Class<?> intf : exposedInterfaces) {
169+
Type intfType = Type.getType(intf);
170+
171+
for (java.lang.reflect.Method m : intf.getMethods())
172+
result.put(Method.getMethod(m), intfType);
173+
}
174+
175+
return result;
176+
}
177+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package openperipheral.interfaces.oc.asm;
2+
3+
import java.lang.reflect.Field;
4+
import java.lang.reflect.Method;
5+
import java.security.ProtectionDomain;
6+
import java.util.Set;
7+
8+
import li.cil.oc.api.network.ManagedEnvironment;
9+
import openperipheral.adapter.IMethodExecutor;
10+
import openperipheral.adapter.WrappedEntityBase;
11+
12+
import com.google.common.base.Throwables;
13+
14+
public class EnvironmentFactory {
15+
16+
private final EnvironmentCodeGenerator generator = new EnvironmentCodeGenerator();
17+
18+
private static Method defineClass;
19+
20+
public Class<? extends ManagedEnvironment> generateEnvironment(String name, Class<?> targetClass, Set<Class<?>> exposedInterfaces, WrappedEntityBase methods) {
21+
try {
22+
byte[] bytes = generator.generate(name, targetClass, exposedInterfaces, methods);
23+
@SuppressWarnings("unchecked")
24+
final Class<? extends ManagedEnvironment> result = (Class<? extends ManagedEnvironment>)defineClass(name, bytes);
25+
setMethodsField(methods.getMethods(), result);
26+
return result;
27+
} catch (Throwable t) {
28+
throw Throwables.propagate(t);
29+
}
30+
}
31+
32+
private static void setMethodsField(IMethodExecutor[] methods, final Class<?> result) throws Exception {
33+
Field methodsField = result.getDeclaredField(EnvironmentBase.METHODS_FIELD);
34+
methodsField.setAccessible(true);
35+
methodsField.set(null, methods);
36+
}
37+
38+
private Class<?> defineClass(String name, byte[] bytes) throws Exception {
39+
final Class<? extends EnvironmentFactory> ownClass = getClass();
40+
final ClassLoader cl = ownClass.getClassLoader();
41+
final ProtectionDomain pd = ownClass.getProtectionDomain();
42+
43+
if (defineClass == null) {
44+
defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class);
45+
defineClass.setAccessible(true);
46+
}
47+
48+
return (Class<?>)defineClass.invoke(cl, name, bytes, 0, bytes.length, pd);
49+
}
50+
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package openperipheral.interfaces.oc.providers;
2+
3+
import java.util.Map;
4+
5+
import li.cil.oc.api.network.ManagedEnvironment;
6+
import net.minecraft.tileentity.TileEntity;
7+
import net.minecraft.world.World;
8+
import openmods.Log;
9+
import openperipheral.adapter.IMethodExecutor;
10+
import openperipheral.adapter.TileEntityBlacklist;
11+
import openperipheral.interfaces.oc.Registries;
12+
13+
import com.google.common.collect.Maps;
14+
15+
public class DriverOpenPeripheral implements li.cil.oc.api.driver.Block {
16+
17+
private final EnvironmentProvider provider = new EnvironmentProvider();
18+
19+
private final Map<Class<?>, Boolean> cache = Maps.newHashMap();
20+
21+
@Override
22+
public boolean worksWith(World world, int x, int y, int z) {
23+
TileEntity te = world.getTileEntity(x, y, z);
24+
if (te == null) return false;
25+
26+
Class<?> cls = te.getClass();
27+
28+
Boolean result = cache.get(te);
29+
30+
if (result == null) {
31+
result = shouldProvide(cls);
32+
cache.put(cls, result);
33+
}
34+
35+
return result;
36+
}
37+
38+
private static boolean shouldProvide(Class<?> cls) {
39+
if (TileEntityBlacklist.INSTANCE.isBlacklisted(cls)) return false;
40+
Map<String, IMethodExecutor> methods = Registries.PERIPHERAL_METHODS_FACTORY.getAdaptedClass(cls);
41+
return !methods.isEmpty();
42+
}
43+
44+
@Override
45+
public ManagedEnvironment createEnvironment(World world, int x, int y, int z) {
46+
TileEntity te = world.getTileEntity(x, y, z);
47+
if (te == null) {
48+
Log.warn("Trying to provide environment for %d,%d,%d in world %d, but TE not found", x, y, z, world.provider.dimensionId);
49+
return null;
50+
}
51+
52+
return provider.createEnvironment(te);
53+
}
54+
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package openperipheral.interfaces.oc.providers;
2+
3+
import java.lang.reflect.Constructor;
4+
import java.util.Map;
5+
import java.util.Set;
6+
7+
import li.cil.oc.api.network.ManagedEnvironment;
8+
import openperipheral.adapter.IMethodExecutor;
9+
import openperipheral.adapter.WrappedEntityBase;
10+
import openperipheral.api.ExposeInterface;
11+
import openperipheral.interfaces.oc.Registries;
12+
import openperipheral.interfaces.oc.asm.EnvironmentFactory;
13+
14+
import com.google.common.base.Preconditions;
15+
import com.google.common.base.Throwables;
16+
import com.google.common.collect.ImmutableSet;
17+
import com.google.common.collect.Maps;
18+
19+
public class EnvironmentProvider {
20+
21+
private final EnvironmentFactory evnClassFactory = new EnvironmentFactory();
22+
23+
private int clsCounter;
24+
25+
private Map<Class<?>, Constructor<? extends ManagedEnvironment>> generatedClasses = Maps.newHashMap();
26+
27+
public Constructor<? extends ManagedEnvironment> getEnvClass(Class<?> targetCls) {
28+
Constructor<? extends ManagedEnvironment> envCtor = generatedClasses.get(targetCls);
29+
30+
if (envCtor == null) {
31+
Map<String, IMethodExecutor> methods = Registries.PERIPHERAL_METHODS_FACTORY.getAdaptedClass(targetCls);
32+
33+
ExposeInterface intfAnnotation = targetCls.getAnnotation(ExposeInterface.class);
34+
35+
Set<Class<?>> exposedInterfaces = intfAnnotation != null? getInterfaces(targetCls, intfAnnotation.value()) : ImmutableSet.<Class<?>> of();
36+
37+
String name = String.format("OpEnvironment$$%04d_%08X", clsCounter++, System.identityHashCode(targetCls));
38+
Class<? extends ManagedEnvironment> cls = evnClassFactory.generateEnvironment(name, targetCls, exposedInterfaces, new WrappedEntityBase(methods));
39+
40+
try {
41+
envCtor = cls.getConstructor(targetCls);
42+
} catch (Exception e) {
43+
throw Throwables.propagate(e);
44+
}
45+
46+
generatedClasses.put(targetCls, envCtor);
47+
}
48+
49+
return envCtor;
50+
}
51+
52+
private static Set<Class<?>> getInterfaces(Class<?> targetClass, Class<?>[] value) {
53+
Set<Class<?>> result = ImmutableSet.copyOf(value);
54+
55+
for (Class<?> intf : result)
56+
Preconditions.checkArgument(intf.isAssignableFrom(targetClass), "Class %s tries to expose interface %s, but does not implement it");
57+
58+
return result;
59+
}
60+
61+
public ManagedEnvironment createEnvironment(Object target) {
62+
Constructor<? extends ManagedEnvironment> envCtor = getEnvClass(target.getClass());
63+
64+
try {
65+
return envCtor.newInstance(target);
66+
} catch (Exception e) {
67+
throw Throwables.propagate(e);
68+
}
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package openperipheral.interfaces.oc.wrappers;
2+
3+
import java.util.Map;
4+
5+
import li.cil.oc.api.machine.Arguments;
6+
import li.cil.oc.api.machine.Context;
7+
import li.cil.oc.api.network.ManagedPeripheral;
8+
import li.cil.oc.api.prefab.AbstractValue;
9+
import openperipheral.adapter.DefaultArgNames;
10+
import openperipheral.adapter.IMethodExecutor;
11+
import openperipheral.interfaces.oc.Registries;
12+
13+
import com.google.common.base.Preconditions;
14+
15+
public class ManagedPeripheralWrapper {
16+
17+
private static class ObjectWrap extends AbstractValue implements ManagedPeripheral {
18+
19+
private final Object target;
20+
21+
private final Map<String, IMethodExecutor> methods;
22+
23+
private final String[] methodsNames;
24+
25+
public ObjectWrap(Object target, Map<String, IMethodExecutor> methods) {
26+
this.target = target;
27+
this.methods = methods;
28+
this.methodsNames = methods.keySet().toArray(new String[methods.size()]);
29+
}
30+
31+
@Override
32+
public String[] methods() {
33+
return methodsNames;
34+
}
35+
36+
@Override
37+
public Object[] invoke(String method, Context context, Arguments args) throws Exception {
38+
IMethodExecutor executor = methods.get(method);
39+
Preconditions.checkArgument(executor != null, "Invalid method name: '%s'", method);
40+
41+
Object[] objArgs = args.toArray();
42+
return executor.startCall(target).setOptionalArg(DefaultArgNames.ARG_CONTEXT, context).call(objArgs);
43+
}
44+
45+
}
46+
47+
public static ManagedPeripheral wrap(Object target) {
48+
Preconditions.checkNotNull(target, "Can't wrap null");
49+
Map<String, IMethodExecutor> methods = Registries.OBJECT_METHODS_FACTORY.getAdaptedClass(target.getClass());
50+
return methods.isEmpty()? null : new ObjectWrap(target, methods);
51+
}
52+
53+
}

‎src/main/java/openperipheral/util/DocBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import openperipheral.adapter.IDescriptable;
1717
import openperipheral.adapter.IMethodExecutor;
18-
import openperipheral.converter.wrappers.AdapterWrapper;
18+
import openperipheral.adapter.wrappers.AdapterWrapper;
1919

2020
import org.w3c.dom.Document;
2121
import org.w3c.dom.Element;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package openperipheral.tests;
2+
3+
import static org.mockito.Matchers.anyInt;
4+
import static org.mockito.Matchers.anyObject;
5+
import static org.mockito.Matchers.anyString;
6+
import static org.mockito.Mockito.mock;
7+
import static org.mockito.Mockito.verify;
8+
import static org.mockito.Mockito.when;
9+
10+
import java.lang.reflect.Method;
11+
import java.util.Map;
12+
13+
import li.cil.oc.api.machine.Arguments;
14+
import li.cil.oc.api.machine.Callback;
15+
import li.cil.oc.api.machine.Context;
16+
import li.cil.oc.api.network.ManagedEnvironment;
17+
import openperipheral.adapter.*;
18+
import openperipheral.interfaces.oc.asm.EnvironmentFactory;
19+
20+
import org.apache.commons.lang3.tuple.Pair;
21+
import org.junit.Assert;
22+
import org.junit.Test;
23+
24+
import com.google.common.collect.ImmutableSet;
25+
import com.google.common.collect.Maps;
26+
27+
public class EnvironmentGeneratorTest {
28+
29+
public static interface InterfaceA {
30+
public void testA(int a);
31+
32+
public String testB(float a);
33+
}
34+
35+
public static interface InterfaceB {
36+
public String testB(float a);
37+
38+
public float testC(int a);
39+
}
40+
41+
private abstract static class TargetClass implements InterfaceA, InterfaceB {
42+
43+
}
44+
45+
private static Method getMethod(Class<?> cls, String prefix) {
46+
for (Method m : cls.getMethods())
47+
if (m.getName().startsWith(prefix)) return m;
48+
49+
throw new IllegalArgumentException();
50+
}
51+
52+
private static void addMethod(Map<String, Pair<IMethodExecutor, IMethodCall>> methods, String name, final boolean isAsynchronous, final String desc) {
53+
IMethodExecutor executor = mock(IMethodExecutor.class);
54+
55+
when(executor.isAsynchronous()).thenReturn(isAsynchronous);
56+
57+
IDescriptable descriptable = mock(IDescriptable.class);
58+
when(descriptable.signature()).thenReturn(desc);
59+
when(executor.description()).thenReturn(descriptable);
60+
61+
IMethodCall call = mock(IMethodCall.class);
62+
when(executor.startCall(anyObject())).thenReturn(call);
63+
64+
methods.put(name, Pair.of(executor, call));
65+
}
66+
67+
private static void testMethod(Object target, Object o, Class<?> cls, String name, IMethodExecutor executor, IMethodCall call) throws Exception {
68+
Method m = getMethod(cls, name.substring(0, 1));
69+
70+
Callback callback = m.getAnnotation(Callback.class);
71+
Assert.assertNotNull(callback);
72+
73+
Assert.assertEquals(executor.isAsynchronous(), callback.direct());
74+
Assert.assertEquals(executor.description().signature(), callback.doc());
75+
76+
Arguments args = mock(Arguments.class);
77+
final Object[] result = new Object[] { 1, 2, 3 };
78+
when(args.toArray()).thenReturn(result);
79+
when(call.setOptionalArg(anyString(), anyObject())).thenReturn(call);
80+
Context context = mock(Context.class);
81+
82+
m.invoke(o, context, args);
83+
84+
verify(executor).startCall(target);
85+
86+
verify(args).toArray();
87+
verify(call).setOptionalArg(DefaultArgNames.ARG_CONTEXT, context);
88+
verify(call).call(result);
89+
}
90+
91+
@Test
92+
public void test() throws Exception {
93+
Map<String, Pair<IMethodExecutor, IMethodCall>> mocks = Maps.newHashMap();
94+
95+
// 7 methods, to generate ICONST_0...ICONST_5 and then LDC 6
96+
97+
addMethod(mocks, "a1", true, "desc1");
98+
addMethod(mocks, "b2_", false, "desc2");
99+
addMethod(mocks, "c3", true, "desc3");
100+
addMethod(mocks, "d 4", false, "desc4");
101+
addMethod(mocks, "e*5", true, "desc5");
102+
addMethod(mocks, "f6", false, "desc6");
103+
addMethod(mocks, "gG", true, "desc-");
104+
105+
Map<String, IMethodExecutor> methods = Maps.newHashMap();
106+
for (Map.Entry<String, Pair<IMethodExecutor, IMethodCall>> e : mocks.entrySet())
107+
methods.put(e.getKey(), e.getValue().getLeft());
108+
109+
EnvironmentFactory generator = new EnvironmentFactory();
110+
111+
Class<?> cls = generator.generateEnvironment("TestClass", TargetClass.class, ImmutableSet.of(InterfaceA.class, InterfaceB.class), new WrappedEntityBase(methods));
112+
113+
final TargetClass target = mock(TargetClass.class);
114+
Object o = cls.getConstructor(TargetClass.class).newInstance(target);
115+
116+
Assert.assertTrue(o instanceof ManagedEnvironment);
117+
Assert.assertTrue(o instanceof InterfaceA);
118+
Assert.assertTrue(o instanceof InterfaceB);
119+
120+
when(target.testB(anyInt())).thenReturn("abcd");
121+
122+
InterfaceA aa = (InterfaceA)o;
123+
Assert.assertEquals(aa.testB(3), "abcd");
124+
verify(target).testB(3);
125+
126+
for (Map.Entry<String, Pair<IMethodExecutor, IMethodCall>> method : mocks.entrySet()) {
127+
final Pair<IMethodExecutor, IMethodCall> value = method.getValue();
128+
testMethod(target, o, cls, method.getKey(), value.getLeft(), value.getRight());
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)
Please sign in to comment.