OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #library('frog_server'); | |
6 | |
7 #import('dart:io'); | |
8 #import('../../lib/json/json.dart'); | |
9 #import('../lang.dart'); | |
10 #import('../file_system_vm.dart'); | |
11 #import('../../lib/utf/utf.dart'); | |
12 | |
13 /// The server socket used to listen for incoming connections. | |
14 ServerSocket serverSocket; | |
15 | |
16 initializeCompiler(String homedir) { | |
17 final filesystem = new VMFileSystem(); | |
18 parseOptions(homedir, [null, null], filesystem); | |
19 initializeWorld(filesystem); | |
20 } | |
21 | |
22 /// Compiles the Dart script at the specified location and saves the file. | |
23 /// Request should look like: | |
24 /// { | |
25 /// "command": "compile", | |
26 /// "id": "anyStringOrNumber", | |
27 /// "input": "/Path/To/main_script.dart", | |
28 /// "output": "/Path/To/main_script.dart.js", | |
29 /// } | |
30 compileCommand(Map request, OutputStream output) { | |
31 var id = request['id']; | |
32 world.reset(); | |
33 options.dartScript = request['input']; | |
34 options.outfile = request['output']; | |
35 if (options.outfile == null) { | |
36 options.checkOnly = true; | |
37 } | |
38 print('starting compile with id $id, ' + | |
39 '${options.dartScript} -> ${options.outfile}'); | |
40 | |
41 world.messageHandler = (String prefix, String message, SourceSpan span) { | |
42 var jsonSpan; | |
43 if (span == null) { | |
44 // Any messages that are not associated with a file become associated with | |
45 // the library file. | |
46 jsonSpan = { 'file': request['input'], 'start': 0, 'end': 0, | |
47 'line': 0, 'column': 0 }; | |
48 } else { | |
49 jsonSpan = { 'file': span.file.filename, 'start': span.start, 'end': span.
end, | |
50 'line': span.line, 'column': span.column }; | |
51 } | |
52 | |
53 writeJson(output, { | |
54 'kind': 'message', | |
55 'id': id, | |
56 'prefix': prefix, | |
57 'message': message, | |
58 'span': jsonSpan | |
59 }); | |
60 }; | |
61 | |
62 bool success = false; | |
63 | |
64 try { | |
65 success = world.compileAndSave(); | |
66 } catch (var exc) { | |
67 writeJson(output, { | |
68 'kind': 'message', | |
69 'id': id, | |
70 'message': "compiler exception: ${exc}" | |
71 }); | |
72 } | |
73 | |
74 writeJson(output, { | |
75 'kind': 'done', | |
76 'command': 'compile', | |
77 'id': id, | |
78 'result': success | |
79 }); | |
80 } | |
81 | |
82 /// Writes the supplied JSON-serializable [obj] to the output stream. | |
83 writeJson(OutputStream output, obj) { | |
84 var jsonBytes = encodeUtf8(JSON.stringify(obj)); | |
85 output.write(int32ToBigEndian(jsonBytes.length), copyBuffer:false); | |
86 output.write(jsonBytes, copyBuffer:false); | |
87 } | |
88 | |
89 List<int> int32ToBigEndian(int len) => | |
90 [(len >> 24) & 0x7F, (len >> 16) & 0xFF, (len >> 8) & 0xFF, len & 0xFF]; | |
91 | |
92 int bigEndianToInt32(List<int> bytes) => | |
93 (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; | |
94 | |
95 /// Reads some JSON, and pops bytes that were read. | |
96 /// Returns `null` if we don't have enough bytes yet. | |
97 tryReadJson(List<int> bytes) { | |
98 if (bytes.length < 4) return; | |
99 int len = bigEndianToInt32(bytes); | |
100 if (bytes.length < 4 + len) { | |
101 print('info: wait for more data (got ${bytes.length}, need ${len + 4})'); | |
102 return null; // wait for more data | |
103 } | |
104 var jsonBytes = bytes.getRange(4, len); | |
105 bytes.removeRange(0, len + 4); | |
106 return JSON.parse(decodeUtf8(jsonBytes)); | |
107 } | |
108 | |
109 /// Tries to handle a request. This routine will remove any bytes it consumes. | |
110 /// If the data is incomplete it's expected to leave them in the list and | |
111 /// return. | |
112 handleRequest(List<int> bytes, Socket socket) { | |
113 var request = tryReadJson(bytes); | |
114 if (request == null) { | |
115 return; // wait for more data | |
116 } | |
117 switch (request['command']) { | |
118 // TODO(jmesserly): split "compile" and "newWorld" commands, add | |
119 // a way to set options. | |
120 case 'compile': | |
121 compileCommand(request, socket.outputStream); | |
122 break; | |
123 case 'close': | |
124 socket.close(); | |
125 break; | |
126 default: | |
127 print('info: unknown command "${request["command"]}"'); | |
128 } | |
129 } | |
130 | |
131 /// Accepts an incoming socket connection | |
132 onConnect(Socket socket) { | |
133 var bytes = new List<int>(); | |
134 socket.onData = () { | |
135 var pos = bytes.length; | |
136 var len = socket.available(); | |
137 bytes.insertRange(pos, len); | |
138 socket.readList(bytes, pos, len); | |
139 handleRequest(bytes, socket); | |
140 }; | |
141 socket.onError = () => socket.close(); | |
142 socket.onClosed = () => socket.close(); | |
143 | |
144 // Close the serverSocket - we only ever service one client. | |
145 serverSocket.close(); | |
146 } | |
147 | |
148 /// This token is used by the editor to know when frogc has successfully come up
. | |
149 final STARTUP_TOKEN = 'frog: accepting connections'; | |
150 | |
151 /// Initialize the server and start listening for requests. Needs hostname/ip | |
152 /// and port that it should listen on, and the Frog library directory. | |
153 ServerSocket startServer(String homedir, String host, int port) { | |
154 // Initialize the compiler. Only need to happen once. | |
155 initializeCompiler(homedir); | |
156 serverSocket = new ServerSocket(host, port, 50); | |
157 serverSocket.onConnection = onConnect; | |
158 print('$STARTUP_TOKEN on $host:${serverSocket.port}'); | |
159 return serverSocket; | |
160 } | |
161 | |
162 /// Main entry point for FrogServer. | |
163 main() { | |
164 // TODO(jmesserly): until we have a way of getting the executable's path, | |
165 // we'll run from current working directory. | |
166 var homedir = new File('.').fullPathSync(); | |
167 | |
168 var argv = new Options().arguments; | |
169 var host = argv.length > 0 ? argv[0] : '127.0.0.1'; | |
170 var port = argv.length > 1 ? Math.parseInt(argv[1]) : 1236; | |
171 startServer(homedir, host, port); | |
172 } | |
OLD | NEW |