Skip to content

Commit f93c49d

Browse files
committed
Added server socket and process pipe to IO.
1 parent bdce26a commit f93c49d

File tree

6 files changed

+246
-21
lines changed

6 files changed

+246
-21
lines changed

src/vm/jvm/QAST/Compiler.nqp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,8 @@ QAST::OperationsJAST.add_core_op('shell', -> $qastcomp, $op {
19191919
?? QAST::Op.new( :op('shell1'), |@operands )
19201920
!! QAST::Op.new( :op('shell3'), |@operands ));
19211921
});
1922+
QAST::OperationsJAST.map_classlib_core_op('spawn', $TYPE_OPS, 'spawn', [$RT_OBJ, $RT_STR, $RT_OBJ], $RT_INT, :tc);
1923+
QAST::OperationsJAST.map_classlib_core_op('openpipe', $TYPE_OPS, 'openpipe', [$RT_STR, $RT_STR, $RT_OBJ], $RT_OBJ, :tc);
19221924

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

@@ -1930,8 +1932,10 @@ QAST::OperationsJAST.map_classlib_core_op('openasync', $TYPE_OPS, 'openasync', [
19301932
QAST::OperationsJAST.map_classlib_core_op('slurpasync', $TYPE_OPS, 'slurpasync', [$RT_OBJ, $RT_OBJ, $RT_OBJ, $RT_OBJ], $RT_OBJ, :tc);
19311933
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);
19321934

1933-
QAST::OperationsJAST.map_classlib_core_op('socket', $TYPE_OPS, 'socket', [], $RT_OBJ, :tc);
1935+
QAST::OperationsJAST.map_classlib_core_op('socket', $TYPE_OPS, 'socket', [$RT_INT], $RT_OBJ, :tc);
19341936
QAST::OperationsJAST.map_classlib_core_op('connect', $TYPE_OPS, 'connect', [$RT_OBJ, $RT_STR, $RT_INT], $RT_OBJ, :tc);
1937+
QAST::OperationsJAST.map_classlib_core_op('bindsock', $TYPE_OPS, 'bindsock', [$RT_OBJ, $RT_STR, $RT_INT], $RT_OBJ, :tc);
1938+
QAST::OperationsJAST.map_classlib_core_op('accept', $TYPE_OPS, 'accept', [$RT_OBJ], $RT_OBJ, :tc);
19351939

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.perl6.nqp.io;
2+
3+
import org.perl6.nqp.runtime.ThreadContext;
4+
5+
public interface IIOBindable {
6+
7+
public void bind(ThreadContext tc, String host, int port);
8+
public SocketHandle accept(ThreadContext tc);
9+
10+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.perl6.nqp.io;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.io.InputStream;
6+
import java.io.OutputStream;
7+
import java.lang.ProcessBuilder.Redirect;
8+
import java.nio.ByteBuffer;
9+
import java.nio.channels.ByteChannel;
10+
import java.nio.channels.Channels;
11+
import java.nio.channels.ReadableByteChannel;
12+
import java.nio.channels.WritableByteChannel;
13+
import java.nio.charset.Charset;
14+
import java.util.Map;
15+
16+
import org.perl6.nqp.runtime.ExceptionHandling;
17+
import org.perl6.nqp.runtime.ThreadContext;
18+
19+
public class ProcessHandle extends SyncHandle {
20+
21+
Process process;
22+
23+
public ProcessHandle(ThreadContext tc, String cmd, String dir, Map<String, String> env) {
24+
ProcessBuilder pb = new ProcessBuilder("sh", "-c", cmd);
25+
pb.directory(new File(dir));
26+
pb.redirectError(Redirect.INHERIT);
27+
28+
// Clear the JVM inherited environment and use provided only
29+
Map<String, String> pbEnv = pb.environment();
30+
pbEnv.clear();
31+
pbEnv.putAll(env);
32+
33+
try {
34+
process = pb.start();
35+
chan = new ProcessChannel(process.getOutputStream(), process.getInputStream());
36+
setEncoding(tc, Charset.forName("UTF-8"));
37+
} catch (IOException e) {
38+
throw ExceptionHandling.dieInternal(tc, e);
39+
}
40+
}
41+
42+
static class ProcessChannel implements ByteChannel {
43+
protected WritableByteChannel stdin;
44+
protected ReadableByteChannel stdout;
45+
46+
public ProcessChannel(OutputStream stdin, InputStream stdout) {
47+
this.stdin = Channels.newChannel(stdin);
48+
this.stdout = Channels.newChannel(stdout);
49+
}
50+
51+
public int read(ByteBuffer dst) throws IOException {
52+
return stdout.read(dst);
53+
}
54+
55+
public boolean isOpen() {
56+
return stdin.isOpen();
57+
}
58+
59+
public void close() throws IOException {
60+
stdin.close();
61+
stdout.close();
62+
}
63+
64+
public int write(ByteBuffer src) throws IOException {
65+
return stdin.write(src);
66+
}
67+
}
68+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.perl6.nqp.io;
2+
3+
import java.io.IOException;
4+
import java.net.InetSocketAddress;
5+
import java.nio.channels.ServerSocketChannel;
6+
import java.nio.channels.SocketChannel;
7+
8+
import org.perl6.nqp.runtime.ExceptionHandling;
9+
import org.perl6.nqp.runtime.ThreadContext;
10+
11+
public class ServerSocketHandle implements IIOBindable {
12+
13+
ServerSocketChannel listenChan;
14+
15+
public ServerSocketHandle(ThreadContext tc) {
16+
try {
17+
listenChan = ServerSocketChannel.open();
18+
} catch (IOException e) {
19+
ExceptionHandling.dieInternal(tc, e);
20+
}
21+
}
22+
23+
public void bind(ThreadContext tc, String host, int port) {
24+
try {
25+
InetSocketAddress addr = new InetSocketAddress(host, port);
26+
listenChan.bind(addr);
27+
} catch (IOException e) {
28+
throw ExceptionHandling.dieInternal(tc, e);
29+
}
30+
}
31+
32+
public SocketHandle accept(ThreadContext tc) {
33+
try {
34+
SocketChannel chan = listenChan.accept();
35+
return chan == null ? null : new SocketHandle(tc, chan);
36+
} catch (IOException e) {
37+
throw ExceptionHandling.dieInternal(tc, e);
38+
}
39+
}
40+
}

src/vm/jvm/runtime/org/perl6/nqp/io/SocketHandle.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public SocketHandle(ThreadContext tc) {
1919
}
2020
}
2121

22+
public SocketHandle(ThreadContext tc, SocketChannel existing) {
23+
chan = existing;
24+
setEncoding(tc, Charset.forName("UTF-8"));
25+
}
26+
2227
public void connect(ThreadContext tc, String host, int port) {
2328
try {
2429
InetSocketAddress addr = new InetSocketAddress(host, port);

src/vm/jvm/runtime/org/perl6/nqp/runtime/Ops.java

Lines changed: 118 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import java.math.BigInteger;
1212
import java.math.RoundingMode;
1313
import java.nio.ByteBuffer;
14-
import java.nio.channels.SocketChannel;
1514
import java.nio.charset.Charset;
1615
import java.nio.file.DirectoryStream;
1716
import java.nio.file.Files;
@@ -43,6 +42,8 @@
4342
import org.perl6.nqp.io.IIOSeekable;
4443
import org.perl6.nqp.io.IIOSyncReadable;
4544
import org.perl6.nqp.io.IIOSyncWritable;
45+
import org.perl6.nqp.io.ProcessHandle;
46+
import org.perl6.nqp.io.ServerSocketHandle;
4647
import org.perl6.nqp.io.SocketHandle;
4748
import org.perl6.nqp.io.StandardReadHandle;
4849
import org.perl6.nqp.io.StandardWriteHandle;
@@ -281,21 +282,59 @@ public static SixModelObject openasync(String path, String mode, ThreadContext t
281282
return h;
282283
}
283284

284-
public static SixModelObject socket(ThreadContext tc) {
285+
public static SixModelObject socket(long listener, ThreadContext tc) {
285286
SixModelObject IOType = tc.curFrame.codeRef.staticInfo.compUnit.hllConfig.ioType;
286287
IOHandleInstance h = (IOHandleInstance)IOType.st.REPR.allocate(tc, IOType.st);
287-
h.handle = new SocketHandle(tc);
288+
if (listener == 0) {
289+
h.handle = new SocketHandle(tc);
290+
} else if (listener > 0) {
291+
h.handle = new ServerSocketHandle(tc);
292+
} else {
293+
ExceptionHandling.dieInternal(tc,
294+
"Socket handle does not support a negative listener value");
295+
}
288296
return h;
289297
}
290298

291299
public static SixModelObject connect(SixModelObject obj, String host, long port, ThreadContext tc) {
292300
IOHandleInstance h = (IOHandleInstance)obj;
293301
if (h.handle instanceof SocketHandle) {
294302
((SocketHandle)h.handle).connect(tc, host, (int) port);
303+
} else {
304+
ExceptionHandling.dieInternal(tc,
305+
"This handle does not support connect");
306+
}
307+
return obj;
308+
}
309+
310+
public static SixModelObject bindsock(SixModelObject obj, String host, long port, ThreadContext tc) {
311+
IOHandleInstance h = (IOHandleInstance)obj;
312+
if (h.handle instanceof ServerSocketHandle) {
313+
((ServerSocketHandle)h.handle).bind(tc, host, (int) port);
314+
} else {
315+
ExceptionHandling.dieInternal(tc,
316+
"This handle does not support bind");
295317
}
296318
return obj;
297319
}
298320

321+
public static SixModelObject accept(SixModelObject obj, ThreadContext tc) {
322+
IOHandleInstance listener = (IOHandleInstance)obj;
323+
if (listener.handle instanceof ServerSocketHandle) {
324+
SocketHandle handle = ((ServerSocketHandle)listener.handle).accept(tc);
325+
if (handle != null) {
326+
SixModelObject IOType = tc.curFrame.codeRef.staticInfo.compUnit.hllConfig.ioType;
327+
IOHandleInstance h = (IOHandleInstance)IOType.st.REPR.allocate(tc, IOType.st);
328+
h.handle = handle;
329+
return h;
330+
}
331+
} else {
332+
ExceptionHandling.dieInternal(tc,
333+
"This handle does not support accept");
334+
}
335+
return null;
336+
}
337+
299338
public static long filereadable(String path, ThreadContext tc) {
300339
Path path_o;
301340
long res;
@@ -714,29 +753,80 @@ public static long link(String before, String after, ThreadContext tc) {
714753
}
715754
return 0;
716755
}
756+
757+
public static SixModelObject openpipe(String cmd, String dir, SixModelObject envObj, ThreadContext tc) {
758+
Map<String, String> env = new HashMap<String, String>();
759+
SixModelObject iter = iter(envObj, tc);
760+
while (istrue(iter, tc) != 0) {
761+
SixModelObject kv = iter.shift_boxed(tc);
762+
String key = iterkey_s(kv, tc);
763+
String value = unbox_s(iterval(kv, tc), tc);
764+
env.put(key, value);
765+
}
766+
767+
SixModelObject IOType = tc.curFrame.codeRef.staticInfo.compUnit.hllConfig.ioType;
768+
IOHandleInstance h = (IOHandleInstance)IOType.st.REPR.allocate(tc, IOType.st);
769+
h.handle = new ProcessHandle(tc, cmd, dir, env);
770+
return h;
771+
}
772+
717773

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

723779
public static long shell3(String cmd, String dir, SixModelObject envObj, ThreadContext tc) {
780+
Map<String, String> env = new HashMap<String, String>();
781+
SixModelObject iter = iter(envObj, tc);
782+
while (istrue(iter, tc) != 0) {
783+
SixModelObject kv = iter.shift_boxed(tc);
784+
String key = iterkey_s(kv, tc);
785+
String value = unbox_s(iterval(kv, tc), tc);
786+
env.put(key, value);
787+
}
788+
789+
List<String> args = new ArrayList<String>();
790+
791+
String os = System.getProperty("os.name").toLowerCase();
792+
if (os.indexOf("win") >= 0) {
793+
args.add("cmd");
794+
args.add("/c");
795+
args.add(cmd.replace('/', '\\'));
796+
} else {
797+
args.add("sh");
798+
args.add("-c");
799+
args.add(cmd);
800+
}
801+
802+
return spawn(args, dir, env);
803+
}
804+
805+
public static long spawn(SixModelObject argsObj, String dir, SixModelObject envObj, ThreadContext tc) {
806+
List<String> args = new ArrayList<String>();
807+
SixModelObject argIter = iter(argsObj, tc);
808+
while (istrue(argIter, tc) != 0) {
809+
SixModelObject v = argIter.shift_boxed(tc);
810+
String arg = v.get_str(tc);
811+
args.add(arg);
812+
}
813+
814+
Map<String, String> env = new HashMap<String, String>();
815+
SixModelObject iter = iter(envObj, tc);
816+
while (istrue(iter, tc) != 0) {
817+
SixModelObject kv = iter.shift_boxed(tc);
818+
String key = iterkey_s(kv, tc);
819+
String value = unbox_s(iterval(kv, tc), tc);
820+
env.put(key, value);
821+
}
822+
823+
return spawn(args, dir , env);
824+
}
825+
826+
private static long spawn(List<String> args, String dir, Map<String, String> env) {
724827
long retval = 255;
725828
try {
726-
Map<String, String> env = new HashMap<String, String>();
727-
728-
SixModelObject iter = iter(envObj, tc);
729-
while (istrue(iter, tc) != 0) {
730-
SixModelObject kv = iter.shift_boxed(tc);
731-
String key = iterkey_s(kv, tc);
732-
String value = unbox_s(iterval(kv, tc), tc);
733-
env.put(key, value);
734-
}
735-
736-
String os = System.getProperty("os.name").toLowerCase();
737-
ProcessBuilder pb = os.indexOf("win") >= 0
738-
? new ProcessBuilder("cmd", "/c", cmd.replace('/', '\\'))
739-
: new ProcessBuilder("sh", "-c", cmd);
829+
ProcessBuilder pb = new ProcessBuilder(args);
740830
pb.directory(new File(dir));
741831

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

747837
Process proc = pb.inheritIO().start();
748-
proc.waitFor();
838+
839+
boolean finished = false;
840+
do {
841+
try {
842+
proc.waitFor();
843+
finished = true;
844+
} catch (InterruptedException e) {
845+
}
846+
} while (!finished);
847+
749848
retval = proc.exitValue();
750849
}
751850
catch (IOException e) {
752851
}
753-
catch (InterruptedException e) {
754-
}
852+
755853
/* Return exit code left shifted by 8 for POSIX emulation. */
756854
return retval << 8;
757855
}

0 commit comments

Comments
 (0)