Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create intermediate builder model for VM and VM-interface to allow creating simple copies #27

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions ssvm-core/src/main/java/dev/xdark/ssvm/VirtualMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,47 @@ public class VirtualMachine implements VMEventCollection {
private volatile InstanceValue systemThreadGroup;
private volatile InstanceValue mainThreadGroup;

/*
* Constructor for copying, see VirtualMachineBuilder
*/
VirtualMachine(VMInterface vmInterface, MemoryAllocator memoryAllocator, ObjectSynchronizer objectSynchronizer,
MemoryManager memoryManager, ClassDefiner classDefiner, ThreadManager threadManager,
FileManager fileManager, NativeLibraryManager nativeLibraryManager, TimeManager timeManager,
ManagementInterface managementInterface, StringPool stringPool, ClassLoaders classLoaders,
ExecutionEngine executionEngine, MirrorFactory mirrorFactory, BootClassFinder bootClassFinder,
ClassStorage classStorage, LinkResolver linkResolver, RuntimeResolver runtimeResolver,
Map<String, String> properties, Map<String, String> env, Reflection reflection, JVMTI jvmti,
VMOperations operations, Symbols symbols, Primitives primitives, InstanceValue systemThreadGroup,
InstanceValue mainThreadGroup) {
this.vmInterface = vmInterface;
this.memoryAllocator = memoryAllocator;
this.objectSynchronizer = objectSynchronizer;
this.memoryManager = memoryManager;
this.classDefiner = classDefiner;
this.threadManager = threadManager.copyForVm(this);
this.fileManager = fileManager;
this.nativeLibraryManager = nativeLibraryManager;
this.timeManager = timeManager;
this.managementInterface = managementInterface;
this.stringPool = stringPool;
this.classLoaders = classLoaders;
this.executionEngine = executionEngine;
this.mirrorFactory = mirrorFactory;
this.bootClassFinder = bootClassFinder;
this.classStorage = classStorage;
this.linkResolver = linkResolver;
this.runtimeResolver = runtimeResolver;
this.properties = properties;
this.env = env;
this.reflection = reflection;
this.jvmti = jvmti;
this.operations = operations;
this.symbols = symbols;
this.primitives = primitives;
this.systemThreadGroup = systemThreadGroup;
this.mainThreadGroup = mainThreadGroup;
}

public VirtualMachine() {
properties = createSystemProperties();
env = createEnvironmentVariables();
Expand Down Expand Up @@ -125,6 +166,13 @@ public VirtualMachine() {
operations = new VMOperations(this);
}

/**
* @return New builder with values copied from this VM.
*/
public VirtualMachineBuilder toBuilder() {
return new VirtualMachineBuilder(this);
}

protected VMInterface createVMInterface() {
return new SimpleVMInterface();
}
Expand Down Expand Up @@ -278,6 +326,13 @@ public VMInterface getInterface() {
return vmInterface;
}

/**
* @return JVMTI manager.
*/
public JVMTI getJvmti() {
return jvmti;
}

/**
* @return New JVMTI environment.
*/
Expand Down
165 changes: 165 additions & 0 deletions ssvm-core/src/main/java/dev/xdark/ssvm/VirtualMachineBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package dev.xdark.ssvm;

import dev.xdark.ssvm.api.VMInterface;
import dev.xdark.ssvm.classloading.BootClassFinder;
import dev.xdark.ssvm.classloading.ClassDefiner;
import dev.xdark.ssvm.classloading.ClassLoaders;
import dev.xdark.ssvm.classloading.ClassStorage;
import dev.xdark.ssvm.execution.ExecutionEngine;
import dev.xdark.ssvm.filesystem.FileManager;
import dev.xdark.ssvm.jni.NativeLibraryManager;
import dev.xdark.ssvm.jvm.ManagementInterface;
import dev.xdark.ssvm.memory.allocation.MemoryAllocator;
import dev.xdark.ssvm.memory.management.MemoryManager;
import dev.xdark.ssvm.memory.management.StringPool;
import dev.xdark.ssvm.mirror.MirrorFactory;
import dev.xdark.ssvm.operation.VMOperations;
import dev.xdark.ssvm.symbol.Primitives;
import dev.xdark.ssvm.symbol.Symbols;
import dev.xdark.ssvm.synchronizer.ObjectSynchronizer;
import dev.xdark.ssvm.thread.ThreadManager;
import dev.xdark.ssvm.timezone.TimeManager;
import dev.xdark.ssvm.util.Reflection;
import dev.xdark.ssvm.value.InstanceValue;

import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
* An intermediate 'builder' for {@link VirtualMachine} which allows swapping out of a few components of an existing VM.
*
* @author Matt Coley
*/
public class VirtualMachineBuilder {
private AtomicReference<InitializationState> state;
private VMInterface vmInterface;
private MemoryAllocator memoryAllocator;
private ObjectSynchronizer objectSynchronizer;
private MemoryManager memoryManager;
private ClassDefiner classDefiner;
private ThreadManager threadManager;
private FileManager fileManager;
private NativeLibraryManager nativeLibraryManager;
private TimeManager timeManager;
private ManagementInterface managementInterface;
private StringPool stringPool;
private ClassLoaders classLoaders;
private ExecutionEngine executionEngine;
private MirrorFactory mirrorFactory;
private BootClassFinder bootClassFinder;
private ClassStorage classStorage;
private LinkResolver linkResolver;
private RuntimeResolver runtimeResolver;
private Map<String, String> properties;
private Map<String, String> env;
private Reflection reflection;
private JVMTI jvmti;
private VMOperations operations;
private Symbols symbols;
private Primitives primitives;
private InstanceValue systemThreadGroup;
private InstanceValue mainThreadGroup;

/**
* @param vm Base VM to copy values from.
*/
VirtualMachineBuilder(VirtualMachine vm) {
this.vmInterface = vm.getInterface();
this.memoryAllocator = vm.getMemoryAllocator();
this.objectSynchronizer = vm.getObjectSynchronizer();
this.memoryManager = vm.getMemoryManager();
this.classDefiner = vm.getClassDefiner();
this.threadManager = vm.getThreadManager();
this.fileManager = vm.getFileManager();
this.nativeLibraryManager = vm.getNativeLibraryManager();
this.timeManager = vm.getTimeManager();
this.managementInterface = vm.getManagementInterface();
this.stringPool = vm.getStringPool();
this.classLoaders = vm.getClassLoaders();
this.executionEngine = vm.getExecutionEngine();
this.mirrorFactory = vm.getMirrorFactory();
this.bootClassFinder = vm.getBootClassFinder();
this.classStorage = vm.getClassStorage();
this.linkResolver = vm.getLinkResolver();
this.runtimeResolver = vm.getRuntimeResolver();
this.properties = vm.getProperties();
this.env = vm.getenv();
this.reflection = vm.getReflection();
this.jvmti = vm.getJvmti();
this.operations = vm.getOperations();
this.symbols = vm.getSymbols();
this.primitives = vm.getPrimitives();
this.systemThreadGroup = vm.getSystemThreadGroup();
this.mainThreadGroup = vm.getMainThreadGroup();
}

/**
* @see VMInterface#copy() Used to deep copy an existing VM interface.
* @param vmInterface New VM interface to use.
* @return Self.
*/
public VirtualMachineBuilder withVmInterface(VMInterface vmInterface) {
this.vmInterface = vmInterface;
return this;
}

/**
* @param fileManager New file manager to use.
* @return Self.
*/
public VirtualMachineBuilder withFileManager(FileManager fileManager) {
this.fileManager = fileManager;
return this;
}

/**
* @param timeManager New time manager to use.
* @return Self.
*/
public VirtualMachineBuilder withTimeManager(TimeManager timeManager) {
this.timeManager = timeManager;
return this;
}

/**
* @param executionEngine New execution engine to use.
* @return Self.
*/
public VirtualMachineBuilder withExecutionEngine(ExecutionEngine executionEngine) {
this.executionEngine = executionEngine;
return this;
}

/**
* @return New VM with values from this builder.
*/
public VirtualMachine build() {
return new VirtualMachine(vmInterface,
memoryAllocator,
objectSynchronizer,
memoryManager,
classDefiner,
threadManager,
fileManager,
nativeLibraryManager,
timeManager,
managementInterface,
stringPool,
classLoaders,
executionEngine,
mirrorFactory,
bootClassFinder,
classStorage,
linkResolver,
runtimeResolver,
properties,
env,
reflection,
jvmti,
operations,
symbols,
primitives,
systemThreadGroup,
mainThreadGroup);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,9 @@ public void handleAbstractMethodError(ExecutionContext<?> ctx) {
public void handleMaxInterations(ExecutionContext<?> ctx) {
delegate.handleMaxInterations(ctx);
}

@Override
public VMInterface copy() {
return delegate.copy();
}
}
49 changes: 41 additions & 8 deletions ssvm-core/src/main/java/dev/xdark/ssvm/api/SimpleVMInterface.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,51 @@
*/
public class SimpleVMInterface implements VMInterface {
private static final int MAX_INSNS = 1024;
private final InstructionProcessor[] processors = new InstructionProcessor[MAX_INSNS];
private final Map<JavaMethod, MethodInvoker> invokerMap = new HashMap<>();
private final List<MethodEnterListener> methodEnters = new ArrayList<>();
private final List<MethodExitListener> methodExits = new ArrayList<>();
private final List<MethodEnterListener> methodEntersView = Collections.unmodifiableList(methodEnters);
private final List<MethodExitListener> methodExitsView = Collections.unmodifiableList(methodExits);
private final List<InstructionInterceptor> instructionInterceptors = new ArrayList<>();
private final List<InstructionInterceptor> instructionInterceptorsView = Collections.unmodifiableList(instructionInterceptors);
private final InstructionProcessor[] processors;
private final Map<JavaMethod, MethodInvoker> invokerMap;
private final List<MethodEnterListener> methodEnters;
private final List<MethodExitListener> methodExits;
private final List<MethodEnterListener> methodEntersView;
private final List<MethodExitListener> methodExitsView;
private final List<InstructionInterceptor> instructionInterceptors;
private final List<InstructionInterceptor> instructionInterceptorsView;
private Consumer<ExecutionContext<?>> linkageErrorHandler = SimpleVMInterface::handleLinkageError0;
private Consumer<ExecutionContext<?>> abstractMethodHandler = SimpleVMInterface::handleAbstractMethodError0;
private Consumer<ExecutionContext<?>> maxIterationsHandler = SimpleVMInterface::handleMaxIterations0;

private SimpleVMInterface(InstructionProcessor<?>[] processors, Map<JavaMethod, MethodInvoker> invokerMap,
List<MethodEnterListener> methodEnters, List<MethodExitListener> methodExits,
List<InstructionInterceptor> instructionInterceptors) {
this.processors = processors;
this.invokerMap = invokerMap;
this.methodEnters = methodEnters;
this.methodExits = methodExits;
this.instructionInterceptors = instructionInterceptors;

methodEntersView = Collections.unmodifiableList(methodEnters);
methodExitsView = Collections.unmodifiableList(methodExits);
instructionInterceptorsView = Collections.unmodifiableList(instructionInterceptors);
}

public SimpleVMInterface() {
this(new InstructionProcessor[MAX_INSNS],
new HashMap<>(),
new ArrayList<>(),
new ArrayList<>(),
new ArrayList<>()
);

Arrays.fill(processors, new UnknownInstructionProcessor());
}

@Override
@SuppressWarnings("unchecked")
public <I extends AbstractInsnNode> InstructionProcessor<I> getProcessor(I insn) {
return processors[insn.getOpcode()];
}

@Override
@SuppressWarnings("unchecked")
public <I extends AbstractInsnNode> InstructionProcessor<I> getProcessor(int opcode) {
return processors[opcode];
}
Expand Down Expand Up @@ -156,6 +179,16 @@ public void handleMaxInterations(ExecutionContext<?> ctx) {
maxIterationsHandler.accept(ctx);
}

@Override
public VMInterface copy() {
SimpleVMInterface copy = new SimpleVMInterface(processors, new HashMap<>(invokerMap),
new ArrayList<>(methodEnters), new ArrayList<>(methodExits),
new ArrayList<>(instructionInterceptors)
);
System.arraycopy(processors, 0, copy.processors, 0, Math.min(processors.length, copy.processors.length));
return copy;
}

// Default impl for handling linkage errors is to throw UnsatisfiedLinkError
private static void handleLinkageError0(ExecutionContext<?> ctx) {
ctx.getOperations().throwException(ctx.getSymbols().java_lang_UnsatisfiedLinkError(), ctx.getMethod().toString());
Expand Down
10 changes: 10 additions & 0 deletions ssvm-core/src/main/java/dev/xdark/ssvm/api/VMInterface.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dev.xdark.ssvm.api;

import dev.xdark.ssvm.VirtualMachine;
import dev.xdark.ssvm.asm.Modifier;
import dev.xdark.ssvm.execution.ExecutionContext;
import dev.xdark.ssvm.execution.InstructionProcessor;
Expand Down Expand Up @@ -175,4 +176,13 @@ public interface VMInterface {
* @param ctx Context of the native method that interpreted {@link Interpreter#getMaxIterations() the maximum number of allowed iterations}.
*/
void handleMaxInterations(ExecutionContext<?> ctx);

/**
* Copying this interface may be viable when combined with {@link VirtualMachine#toBuilder()} to create basic
* <i>"scopes"</i>. Each scope can have its own set of listeners and interceptors, while not polluting other
* {@link VMInterface} instances.
*
* @return Copy of this interface.
*/
VMInterface copy();
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package dev.xdark.ssvm.execution;

import dev.xdark.ssvm.VirtualMachine;
import dev.xdark.ssvm.mirror.member.JavaMethod;
import dev.xdark.ssvm.mirror.type.InstanceClass;
import dev.xdark.ssvm.operation.VMOperations;
import dev.xdark.ssvm.util.VMFunctions;
import dev.xdark.ssvm.value.ObjectValue;
import dev.xdark.ssvm.value.sink.ValueSink;
Expand Down Expand Up @@ -114,12 +112,4 @@ default InstanceClass getOwner() {
default ObjectValue getClassLoader() {
return getOwner().getClassLoader();
}

/**
* @return VM instance in which method is being executed.
*/
@Override
default VirtualMachine getVM() {
return getOwner().getVM();
}
}
Loading
Loading