diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d8014f4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,28 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+/.project
+/.classpath
+/.settings
+/target
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..98b2d48
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+BSD 3-Clause License
+
+Copyright (c) 2023, Johannes Kuhn
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0c56ad0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# lamarr
+Attach JShell to an existing Java Process
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..8ea7a31
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,35 @@
+
+ 4.0.0
+ pw.dasbrain
+ lamarr
+ 1.0
+ Attach JShell to existing Java Processes
+ jar
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.3.0
+
+
+ true
+
+ pw.dasbrain.lamarr.agent.AgentMain
+ true
+
+
+
+
+
+
+
+ 9
+ 9
+ 9
+ UTF-8
+ UTF-8
+
+
\ No newline at end of file
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..384a7a4
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,12 @@
+module pw.dasbrain.lamarr {
+
+ requires transitive java.instrument;
+ requires jdk.jshell;
+ requires jdk.attach;
+
+ exports pw.dasbrain.lamarr.agent to java.instrument;
+ opens pw.dasbrain.lamarr.agent to java.instrument;
+
+ provides jdk.jshell.spi.ExecutionControlProvider with
+ pw.dasbrain.lamarr.InstrumentationExecutionControlProvider;
+}
\ No newline at end of file
diff --git a/src/main/java/pw/dasbrain/lamarr/InstrumentationExecutionControlProvider.java b/src/main/java/pw/dasbrain/lamarr/InstrumentationExecutionControlProvider.java
new file mode 100644
index 0000000..f270d10
--- /dev/null
+++ b/src/main/java/pw/dasbrain/lamarr/InstrumentationExecutionControlProvider.java
@@ -0,0 +1,61 @@
+package pw.dasbrain.lamarr;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.sun.tools.attach.AgentInitializationException;
+import com.sun.tools.attach.AgentLoadException;
+import com.sun.tools.attach.AttachNotSupportedException;
+import com.sun.tools.attach.VirtualMachine;
+
+import jdk.jshell.execution.StreamingExecutionControl;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControlProvider;
+import jdk.jshell.spi.ExecutionEnv;
+
+public class InstrumentationExecutionControlProvider implements ExecutionControlProvider {
+
+ @Override
+ public String name() {
+ return "instrumentation";
+ }
+
+ @Override
+ public ExecutionControl generate(ExecutionEnv env, Map parameters)
+ throws Throwable {
+ ServerSocket so = new ServerSocket(0);
+ so.setSoTimeout(5000);
+ attach(parameters.get("pid"), so.getLocalPort());
+ Socket s = so.accept();
+ ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream());
+ out.flush();
+ ObjectInputStream in = new ObjectInputStream(s.getInputStream());
+ return new StreamingExecutionControl(out, in);
+ }
+
+ @Override
+ public Map defaultParameters() {
+ Map result = new HashMap<>();
+ result.put("pid", "");
+ return result;
+ }
+
+ private static void attach(String pid, int port) throws AttachNotSupportedException,
+ IOException, AgentLoadException, AgentInitializationException, URISyntaxException {
+ VirtualMachine vm = VirtualMachine.attach(pid);
+ try {
+ vm.loadAgent(new File(InstrumentationExecutionControlProvider.class
+ .getProtectionDomain().getCodeSource().getLocation().toURI()).toString(),
+ port + "");
+ } finally {
+ vm.detach();
+ }
+ }
+}
diff --git a/src/main/java/pw/dasbrain/lamarr/agent/AgentMain.java b/src/main/java/pw/dasbrain/lamarr/agent/AgentMain.java
new file mode 100644
index 0000000..e04a442
--- /dev/null
+++ b/src/main/java/pw/dasbrain/lamarr/agent/AgentMain.java
@@ -0,0 +1,12 @@
+package pw.dasbrain.lamarr.agent;
+
+import java.lang.instrument.Instrumentation;
+
+public class AgentMain {
+
+ public static void agentmain(String arg, Instrumentation inst) {
+ Thread t = new Thread(new InstrumentationRemoteExecutionControl(inst, arg), "rjshell");
+ t.setDaemon(false);
+ t.start();
+ }
+}
diff --git a/src/main/java/pw/dasbrain/lamarr/agent/InstrumentationRemoteExecutionControl.java b/src/main/java/pw/dasbrain/lamarr/agent/InstrumentationRemoteExecutionControl.java
new file mode 100644
index 0000000..c0315d8
--- /dev/null
+++ b/src/main/java/pw/dasbrain/lamarr/agent/InstrumentationRemoteExecutionControl.java
@@ -0,0 +1,56 @@
+package pw.dasbrain.lamarr.agent;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.instrument.ClassDefinition;
+import java.lang.instrument.Instrumentation;
+import java.lang.instrument.UnmodifiableClassException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import jdk.jshell.execution.RemoteExecutionControl;
+import jdk.jshell.execution.Util;
+
+class InstrumentationRemoteExecutionControl extends RemoteExecutionControl implements Runnable {
+
+ private final Instrumentation inst;
+ private final String arg;
+
+ public InstrumentationRemoteExecutionControl(Instrumentation inst, String arg) {
+ this.inst = inst;
+ this.arg = arg;
+ }
+
+ @Override
+ public void redefine(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException {
+ try {
+ ClassDefinition[] defs = new ClassDefinition[cbcs.length];
+ for (int i = 0; i < cbcs.length; i++) {
+ defs[i] = new ClassDefinition(findClass(cbcs[i].name()), cbcs[i].bytecodes());
+ }
+ inst.redefineClasses(defs);
+ } catch (ClassNotFoundException | UnmodifiableClassException
+ | UnsupportedOperationException e) {
+ // In this case no classes have been redefined.
+ ClassInstallException cie = new ClassInstallException(e.getMessage(), new boolean[cbcs.length]);
+ cie.initCause(e);
+ throw cie;
+ }
+ super.redefine(cbcs);
+ }
+
+ @Override
+ public void run() {
+ try (Socket s = new Socket(InetAddress.getLoopbackAddress(), Integer.parseInt(arg));
+ ObjectOutputStream out = new ObjectOutputStream(s.getOutputStream());
+ ObjectInputStream in = new ObjectInputStream(s.getInputStream());) {
+ out.flush();
+ Util.forwardExecutionControl(this, in, out);
+ } catch (IOException e) {
+ // TODO: Better error handling
+ throw new InternalError(e);
+ }
+ }
+}