Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Added server socket and process pipe to IO.
  • Loading branch information
donaldh committed Sep 4, 2013
1 parent 59fb7d2 commit 66c9113
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 21 deletions.
6 changes: 5 additions & 1 deletion src/vm/jvm/QAST/Compiler.nqp
Expand Up @@ -1919,6 +1919,8 @@ QAST::OperationsJAST.add_core_op('shell', -> $qastcomp, $op {
?? QAST::Op.new( :op('shell1'), |@operands )
!! QAST::Op.new( :op('shell3'), |@operands ));
});
QAST::OperationsJAST.map_classlib_core_op('spawn', $TYPE_OPS, 'spawn', [$RT_OBJ, $RT_STR, $RT_OBJ], $RT_INT, :tc);
QAST::OperationsJAST.map_classlib_core_op('openpipe', $TYPE_OPS, 'openpipe', [$RT_STR, $RT_STR, $RT_OBJ], $RT_OBJ, :tc);

QAST::OperationsJAST.map_classlib_core_op('symlink', $TYPE_OPS, 'symlink', [$RT_STR, $RT_STR], $RT_INT, :tc);

Expand All @@ -1930,8 +1932,10 @@ QAST::OperationsJAST.map_classlib_core_op('openasync', $TYPE_OPS, 'openasync', [
QAST::OperationsJAST.map_classlib_core_op('slurpasync', $TYPE_OPS, 'slurpasync', [$RT_OBJ, $RT_OBJ, $RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('linesasync', $TYPE_OPS, 'linesasync', [$RT_OBJ, $RT_OBJ, $RT_INT, $RT_OBJ, $RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);

QAST::OperationsJAST.map_classlib_core_op('socket', $TYPE_OPS, 'socket', [], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('socket', $TYPE_OPS, 'socket', [$RT_INT], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('connect', $TYPE_OPS, 'connect', [$RT_OBJ, $RT_STR, $RT_INT], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('bindsock', $TYPE_OPS, 'bindsock', [$RT_OBJ, $RT_STR, $RT_INT], $RT_OBJ, :tc);
QAST::OperationsJAST.map_classlib_core_op('accept', $TYPE_OPS, 'accept', [$RT_OBJ], $RT_OBJ, :tc);

QAST::OperationsJAST.map_classlib_core_op('debugnoop', $TYPE_OPS, 'debugnoop', [$RT_OBJ], $RT_OBJ, :tc);

Expand Down
10 changes: 10 additions & 0 deletions src/vm/jvm/runtime/org/perl6/nqp/io/IIOBindable.java
@@ -0,0 +1,10 @@
package org.perl6.nqp.io;

import org.perl6.nqp.runtime.ThreadContext;

public interface IIOBindable {

public void bind(ThreadContext tc, String host, int port);
public SocketHandle accept(ThreadContext tc);

}
68 changes: 68 additions & 0 deletions src/vm/jvm/runtime/org/perl6/nqp/io/ProcessHandle.java
@@ -0,0 +1,68 @@
package org.perl6.nqp.io;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ProcessBuilder.Redirect;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.util.Map;

import org.perl6.nqp.runtime.ExceptionHandling;
import org.perl6.nqp.runtime.ThreadContext;

public class ProcessHandle extends SyncHandle {

Process process;

public ProcessHandle(ThreadContext tc, String cmd, String dir, Map<String, String> env) {
ProcessBuilder pb = new ProcessBuilder("sh", "-c", cmd);
pb.directory(new File(dir));
pb.redirectError(Redirect.INHERIT);

// Clear the JVM inherited environment and use provided only
Map<String, String> pbEnv = pb.environment();
pbEnv.clear();
pbEnv.putAll(env);

try {
process = pb.start();
chan = new ProcessChannel(process.getOutputStream(), process.getInputStream());
setEncoding(tc, Charset.forName("UTF-8"));
} catch (IOException e) {
throw ExceptionHandling.dieInternal(tc, e);
}
}

static class ProcessChannel implements ByteChannel {
protected WritableByteChannel stdin;
protected ReadableByteChannel stdout;

public ProcessChannel(OutputStream stdin, InputStream stdout) {
this.stdin = Channels.newChannel(stdin);
this.stdout = Channels.newChannel(stdout);
}

public int read(ByteBuffer dst) throws IOException {
return stdout.read(dst);
}

public boolean isOpen() {
return stdin.isOpen();
}

public void close() throws IOException {
stdin.close();
stdout.close();
}

public int write(ByteBuffer src) throws IOException {
return stdin.write(src);
}
}
}
40 changes: 40 additions & 0 deletions src/vm/jvm/runtime/org/perl6/nqp/io/ServerSocketHandle.java
@@ -0,0 +1,40 @@
package org.perl6.nqp.io;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

import org.perl6.nqp.runtime.ExceptionHandling;
import org.perl6.nqp.runtime.ThreadContext;

public class ServerSocketHandle implements IIOBindable {

ServerSocketChannel listenChan;

public ServerSocketHandle(ThreadContext tc) {
try {
listenChan = ServerSocketChannel.open();
} catch (IOException e) {
ExceptionHandling.dieInternal(tc, e);
}
}

public void bind(ThreadContext tc, String host, int port) {
try {
InetSocketAddress addr = new InetSocketAddress(host, port);
listenChan.bind(addr);
} catch (IOException e) {
throw ExceptionHandling.dieInternal(tc, e);
}
}

public SocketHandle accept(ThreadContext tc) {
try {
SocketChannel chan = listenChan.accept();
return chan == null ? null : new SocketHandle(tc, chan);
} catch (IOException e) {
throw ExceptionHandling.dieInternal(tc, e);
}
}
}
5 changes: 5 additions & 0 deletions src/vm/jvm/runtime/org/perl6/nqp/io/SocketHandle.java
Expand Up @@ -19,6 +19,11 @@ public SocketHandle(ThreadContext tc) {
}
}

public SocketHandle(ThreadContext tc, SocketChannel existing) {
chan = existing;
setEncoding(tc, Charset.forName("UTF-8"));
}

public void connect(ThreadContext tc, String host, int port) {
try {
InetSocketAddress addr = new InetSocketAddress(host, port);
Expand Down
138 changes: 118 additions & 20 deletions src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java
Expand Up @@ -11,7 +11,6 @@
import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
Expand Down Expand Up @@ -43,6 +42,8 @@
import org.perl6.nqp.io.IIOSeekable;
import org.perl6.nqp.io.IIOSyncReadable;
import org.perl6.nqp.io.IIOSyncWritable;
import org.perl6.nqp.io.ProcessHandle;
import org.perl6.nqp.io.ServerSocketHandle;
import org.perl6.nqp.io.SocketHandle;
import org.perl6.nqp.io.StandardReadHandle;
import org.perl6.nqp.io.StandardWriteHandle;
Expand Down Expand Up @@ -281,21 +282,59 @@ public static SixModelObject openasync(String path, String mode, ThreadContext t
return h;
}

public static SixModelObject socket(ThreadContext tc) {
public static SixModelObject socket(long listener, ThreadContext tc) {
SixModelObject IOType = tc.curFrame.codeRef.staticInfo.compUnit.hllConfig.ioType;
IOHandleInstance h = (IOHandleInstance)IOType.st.REPR.allocate(tc, IOType.st);
h.handle = new SocketHandle(tc);
if (listener == 0) {
h.handle = new SocketHandle(tc);
} else if (listener > 0) {
h.handle = new ServerSocketHandle(tc);
} else {
ExceptionHandling.dieInternal(tc,
"Socket handle does not support a negative listener value");
}
return h;
}

public static SixModelObject connect(SixModelObject obj, String host, long port, ThreadContext tc) {
IOHandleInstance h = (IOHandleInstance)obj;
if (h.handle instanceof SocketHandle) {
((SocketHandle)h.handle).connect(tc, host, (int) port);
} else {
ExceptionHandling.dieInternal(tc,
"This handle does not support connect");
}
return obj;
}

public static SixModelObject bindsock(SixModelObject obj, String host, long port, ThreadContext tc) {
IOHandleInstance h = (IOHandleInstance)obj;
if (h.handle instanceof ServerSocketHandle) {
((ServerSocketHandle)h.handle).bind(tc, host, (int) port);
} else {
ExceptionHandling.dieInternal(tc,
"This handle does not support bind");
}
return obj;
}

public static SixModelObject accept(SixModelObject obj, ThreadContext tc) {
IOHandleInstance listener = (IOHandleInstance)obj;
if (listener.handle instanceof ServerSocketHandle) {
SocketHandle handle = ((ServerSocketHandle)listener.handle).accept(tc);
if (handle != null) {
SixModelObject IOType = tc.curFrame.codeRef.staticInfo.compUnit.hllConfig.ioType;
IOHandleInstance h = (IOHandleInstance)IOType.st.REPR.allocate(tc, IOType.st);
h.handle = handle;
return h;
}
} else {
ExceptionHandling.dieInternal(tc,
"This handle does not support accept");
}
return null;
}

public static long filereadable(String path, ThreadContext tc) {
Path path_o;
long res;
Expand Down Expand Up @@ -714,29 +753,80 @@ public static long link(String before, String after, ThreadContext tc) {
}
return 0;
}

public static SixModelObject openpipe(String cmd, String dir, SixModelObject envObj, ThreadContext tc) {
Map<String, String> env = new HashMap<String, String>();
SixModelObject iter = iter(envObj, tc);
while (istrue(iter, tc) != 0) {
SixModelObject kv = iter.shift_boxed(tc);
String key = iterkey_s(kv, tc);
String value = unbox_s(iterval(kv, tc), tc);
env.put(key, value);
}

SixModelObject IOType = tc.curFrame.codeRef.staticInfo.compUnit.hllConfig.ioType;
IOHandleInstance h = (IOHandleInstance)IOType.st.REPR.allocate(tc, IOType.st);
h.handle = new ProcessHandle(tc, cmd, dir, env);
return h;
}


// To be removed once shell3 is adopted
public static long shell1(String cmd, ThreadContext tc) {
return shell3(cmd, cwd(), getenvhash(tc), tc);
}

public static long shell3(String cmd, String dir, SixModelObject envObj, ThreadContext tc) {
Map<String, String> env = new HashMap<String, String>();
SixModelObject iter = iter(envObj, tc);
while (istrue(iter, tc) != 0) {
SixModelObject kv = iter.shift_boxed(tc);
String key = iterkey_s(kv, tc);
String value = unbox_s(iterval(kv, tc), tc);
env.put(key, value);
}

List<String> args = new ArrayList<String>();

String os = System.getProperty("os.name").toLowerCase();
if (os.indexOf("win") >= 0) {
args.add("cmd");
args.add("/c");
args.add(cmd.replace('/', '\\'));
} else {
args.add("sh");
args.add("-c");
args.add(cmd);
}

return spawn(args, dir, env);
}

public static long spawn(SixModelObject argsObj, String dir, SixModelObject envObj, ThreadContext tc) {
List<String> args = new ArrayList<String>();
SixModelObject argIter = iter(argsObj, tc);
while (istrue(argIter, tc) != 0) {
SixModelObject v = argIter.shift_boxed(tc);
String arg = v.get_str(tc);
args.add(arg);
}

Map<String, String> env = new HashMap<String, String>();
SixModelObject iter = iter(envObj, tc);
while (istrue(iter, tc) != 0) {
SixModelObject kv = iter.shift_boxed(tc);
String key = iterkey_s(kv, tc);
String value = unbox_s(iterval(kv, tc), tc);
env.put(key, value);
}

return spawn(args, dir , env);
}

private static long spawn(List<String> args, String dir, Map<String, String> env) {
long retval = 255;
try {
Map<String, String> env = new HashMap<String, String>();

SixModelObject iter = iter(envObj, tc);
while (istrue(iter, tc) != 0) {
SixModelObject kv = iter.shift_boxed(tc);
String key = iterkey_s(kv, tc);
String value = unbox_s(iterval(kv, tc), tc);
env.put(key, value);
}

String os = System.getProperty("os.name").toLowerCase();
ProcessBuilder pb = os.indexOf("win") >= 0
? new ProcessBuilder("cmd", "/c", cmd.replace('/', '\\'))
: new ProcessBuilder("sh", "-c", cmd);
ProcessBuilder pb = new ProcessBuilder(args);
pb.directory(new File(dir));

// Clear the JVM inherited environment and use provided only
Expand All @@ -745,13 +835,21 @@ public static long shell3(String cmd, String dir, SixModelObject envObj, ThreadC
pbEnv.putAll(env);

Process proc = pb.inheritIO().start();
proc.waitFor();

boolean finished = false;
do {
try {
proc.waitFor();
finished = true;
} catch (InterruptedException e) {
}
} while (!finished);

retval = proc.exitValue();
}
catch (IOException e) {
}
catch (InterruptedException e) {
}

/* Return exit code left shifted by 8 for POSIX emulation. */
return retval << 8;
}
Expand Down

0 comments on commit 66c9113

Please sign in to comment.