1 /*
2 Abstract interpreter interface.
3 Copyright (c) 2003 2004 stefan kersten.
4
5 ====================================================================
6
7 SuperCollider real time audio synthesis system
8 Copyright (c) 2002 James McCartney. All rights reserved.
9 http://www.audiosynth.com
10
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26 #include "SC_LanguageClient.h"
27 #include "SC_Lock.h"
28 #include <cstring>
29 #include <string>
30 #include <cerrno>
31
32 #ifdef _WIN32
33 # include <stdio.h>
34 # include <direct.h>
35 # define snprintf _snprintf
36 # ifndef PATH_MAX
37 # define PATH_MAX _MAX_PATH
38 # endif
39 #else
40 # include <unistd.h>
41 #endif
42
43 #include "PyrObject.h"
44 #include "PyrKernel.h"
45 #include "PyrPrimitive.h"
46 #include "PyrSched.h"
47 #include "GC.h"
48 #include "VMGlobals.h"
49 #include "SCBase.h"
50 #include "SC_StringBuffer.h"
51
52 void closeAllGUIScreens();
53 void initGUI();
54 void initGUIPrimitives();
55
56 extern PyrString* newPyrStringN(class PyrGC* gc, long length, long flags, long collect);
57
58 // =====================================================================
59 // SC_LanguageClient
60 // =====================================================================
61
62 SC_LanguageClient* gInstance = nullptr;
63 SC_Lock gInstanceMutex;
64
65 class HiddenLanguageClient {
66 public:
HiddenLanguageClient()67 HiddenLanguageClient(): mPostFile(nullptr), mScratch(0), mRunning(false) {}
68
69 std::string mName;
70 FILE* mPostFile;
71 SC_StringBuffer mScratch;
72 bool mRunning;
73 };
74
SC_LanguageClient(const char * name)75 SC_LanguageClient::SC_LanguageClient(const char* name) {
76 mHiddenClient = new HiddenLanguageClient;
77
78 lockInstance();
79
80 if (gInstance) {
81 unlockInstance();
82 fprintf(stderr, "SC_LanguageClient already running\n");
83 abort();
84 }
85
86 mHiddenClient->mName = name;
87 gInstance = this;
88
89 unlockInstance();
90 }
91
~SC_LanguageClient()92 SC_LanguageClient::~SC_LanguageClient() {
93 lockInstance();
94 gInstance = nullptr;
95 delete mHiddenClient;
96 unlockInstance();
97 }
98
initRuntime(const Options & opt)99 void SC_LanguageClient::initRuntime(const Options& opt) {
100 // start virtual machine
101 if (!mHiddenClient->mRunning) {
102 mHiddenClient->mRunning = true;
103 if (opt.mRuntimeDir) {
104 int err = chdir(opt.mRuntimeDir);
105 if (err)
106 error("Cannot change to runtime directory: %s", strerror(errno));
107 }
108 pyr_init_mem_pools(opt.mMemSpace, opt.mMemGrow);
109 init_OSC(opt.mPort);
110 schedInit();
111 onInitRuntime();
112 }
113 }
114
shutdownRuntime()115 void SC_LanguageClient::shutdownRuntime() {
116 schedCleanup();
117 cleanup_OSC();
118 }
119
compileLibrary(bool standalone)120 void SC_LanguageClient::compileLibrary(bool standalone) { ::compileLibrary(standalone); }
121
122 extern void shutdownLibrary();
shutdownLibrary()123 void SC_LanguageClient::shutdownLibrary() {
124 ::shutdownLibrary();
125 flush();
126 }
127
recompileLibrary(bool standalone)128 void SC_LanguageClient::recompileLibrary(bool standalone) { compileLibrary(standalone); }
129
setCmdLine(const char * buf,size_t size)130 void SC_LanguageClient::setCmdLine(const char* buf, size_t size) {
131 if (isLibraryCompiled()) {
132 lock();
133 if (isLibraryCompiled()) {
134 VMGlobals* g = gMainVMGlobals;
135
136 PyrString* strobj = newPyrStringN(g->gc, size, 0, true);
137 memcpy(strobj->s, buf, size);
138
139 SetObject(&slotRawInterpreter(&g->process->interpreter)->cmdLine, strobj);
140 g->gc->GCWriteNew(slotRawObject(&g->process->interpreter),
141 strobj); // we know strobj is white so we can use GCWriteNew
142 }
143 unlock();
144 }
145 }
146
setCmdLine(const char * str)147 void SC_LanguageClient::setCmdLine(const char* str) { setCmdLine(str, strlen(str)); }
148
setCmdLinef(const char * fmt,...)149 void SC_LanguageClient::setCmdLinef(const char* fmt, ...) {
150 SC_StringBuffer& scratch = mHiddenClient->mScratch;
151 va_list ap;
152 va_start(ap, fmt);
153 scratch.reset();
154 scratch.vappendf(fmt, ap);
155 va_end(ap);
156 setCmdLine(scratch.getData());
157 }
158
runLibrary(PyrSymbol * symbol)159 void SC_LanguageClient::runLibrary(PyrSymbol* symbol) {
160 lock();
161 ::runLibrary(symbol);
162 unlock();
163 }
164
runLibrary(const char * methodName)165 void SC_LanguageClient::runLibrary(const char* methodName) {
166 lock();
167 ::runLibrary(getsym(methodName));
168 unlock();
169 }
170
executeFile(const char * fileName)171 void SC_LanguageClient::executeFile(const char* fileName) {
172 std::string escaped_file_name(fileName);
173 int i = 0;
174 while (i < escaped_file_name.size()) {
175 if (escaped_file_name[i] == '\\')
176 escaped_file_name.insert(++i, 1, '\\');
177 ++i;
178 }
179
180 setCmdLinef("thisProcess.interpreter.executeFile(\"%s\")", escaped_file_name.c_str());
181 runLibrary(s_interpretCmdLine);
182 }
183
snprintMemArg(char * dst,size_t size,int arg)184 void SC_LanguageClient::snprintMemArg(char* dst, size_t size, int arg) {
185 int rem = arg;
186 int mod = 0;
187 const char* modstr = "";
188
189 while (((rem % 1024) == 0) && (mod < 4)) {
190 rem /= 1024;
191 mod++;
192 }
193
194 switch (mod) {
195 case 0:
196 modstr = "";
197 break;
198 case 1:
199 modstr = "k";
200 break;
201 case 2:
202 modstr = "m";
203 break;
204 case 3:
205 modstr = "g";
206 break;
207 default:
208 rem = arg;
209 modstr = "";
210 break;
211 }
212
213 snprintf(dst, size, "%d%s", rem, modstr);
214 }
215
parseMemArg(const char * arg,int * res)216 bool SC_LanguageClient::parseMemArg(const char* arg, int* res) {
217 long value, factor = 1;
218 char* endPtr = nullptr;
219
220 if (*arg == '\0')
221 return false;
222
223 value = strtol(arg, &endPtr, 0);
224
225 char spec = *endPtr++;
226 if (spec != '\0') {
227 if (*endPtr != '\0')
228 // trailing characters
229 return false;
230
231 switch (spec) {
232 case 'k':
233 factor = 1024;
234 break;
235 case 'm':
236 factor = 1024 * 1024;
237 break;
238 default:
239 // invalid mem spec
240 return false;
241 }
242 }
243
244 *res = value * factor;
245
246 return true;
247 }
248
parsePortArg(const char * arg,int * res)249 bool SC_LanguageClient::parsePortArg(const char* arg, int* res) {
250 long value;
251 char* endPtr;
252
253 if (*arg == '\0')
254 return false;
255
256 value = strtol(arg, &endPtr, 0);
257
258 if ((*endPtr != '\0') || (value < 0) || (value > 65535))
259 return false;
260
261 *res = value;
262
263 return true;
264 }
265
tick()266 void SC_LanguageClient::tick() {
267 if (trylock()) {
268 if (isLibraryCompiled()) {
269 ::runLibrary(s_tick);
270 }
271 unlock();
272 }
273 flush();
274 }
275
tickLocked(double * nextTime)276 bool SC_LanguageClient::tickLocked(double* nextTime) {
277 if (isLibraryCompiled()) {
278 ::runLibrary(s_tick);
279 }
280
281 return slotDoubleVal(&gMainVMGlobals->result, nextTime) == errNone;
282 }
283
onInitRuntime()284 void SC_LanguageClient::onInitRuntime() {}
285
onLibraryStartup()286 void SC_LanguageClient::onLibraryStartup() {}
287
onLibraryShutdown()288 void SC_LanguageClient::onLibraryShutdown() {}
289
onInterpStartup()290 void SC_LanguageClient::onInterpStartup() {}
291
292 // runLibrary methods
interpretCmdLine()293 void SC_LanguageClient::interpretCmdLine() { runLibrary(s_interpretCmdLine); }
interpretPrintCmdLine()294 void SC_LanguageClient::interpretPrintCmdLine() { runLibrary(s_interpretPrintCmdLine); }
runMain()295 void SC_LanguageClient::runMain() { runLibrary(s_run); }
stopMain()296 void SC_LanguageClient::stopMain() { runLibrary(s_stop); }
297
298 // locks
lock()299 void SC_LanguageClient::lock() { gLangMutex.lock(); }
trylock()300 bool SC_LanguageClient::trylock() { return gLangMutex.try_lock(); }
unlock()301 void SC_LanguageClient::unlock() { gLangMutex.unlock(); }
302
instance()303 SC_LanguageClient* SC_LanguageClient::instance() { return gInstance; }
304
lockInstance()305 void SC_LanguageClient::lockInstance() { gInstanceMutex.lock(); }
unlockInstance()306 void SC_LanguageClient::unlockInstance() { gInstanceMutex.unlock(); }
307
308 extern bool compiledOK;
309
getName() const310 const char* SC_LanguageClient::getName() const { return mHiddenClient->mName.c_str(); }
311
getPostFile()312 FILE* SC_LanguageClient::getPostFile() { return mHiddenClient->mPostFile; }
setPostFile(FILE * file)313 void SC_LanguageClient::setPostFile(FILE* file) { mHiddenClient->mPostFile = file; }
314
isLibraryCompiled()315 bool SC_LanguageClient::isLibraryCompiled() { return compiledOK; }
316
run(int argc,char ** argv)317 int SC_LanguageClient::run(int argc, char** argv) {
318 throw std::runtime_error("SC_LanguageClient::run only supported on terminal client");
319 }
320
321 // =====================================================================
322 // library functions
323 // =====================================================================
324
setPostFile(FILE * file)325 void setPostFile(FILE* file) { SC_LanguageClient::instance()->setPostFile(file); }
326
vpost(const char * fmt,va_list ap)327 int vpost(const char* fmt, va_list ap) {
328 char buf[512];
329 int n = vsnprintf(buf, sizeof(buf), fmt, ap);
330 if (n > 0) {
331 SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
332 if (client)
333 client->postText(buf, sc_min(n, sizeof(buf) - 1));
334 SC_LanguageClient::unlockInstance();
335 }
336 return 0;
337 }
338
post(const char * fmt,...)339 void post(const char* fmt, ...) {
340 va_list ap;
341 va_start(ap, fmt);
342 vpost(fmt, ap);
343 va_end(ap);
344 }
345
postfl(const char * fmt,...)346 void postfl(const char* fmt, ...) {
347 char buf[512];
348 va_list ap;
349 va_start(ap, fmt);
350 int n = vsnprintf(buf, sizeof(buf), fmt, ap);
351 if (n > 0) {
352 SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
353 if (client)
354 client->postFlush(buf, sc_min(n, sizeof(buf) - 1));
355 SC_LanguageClient::unlockInstance();
356 }
357 va_end(ap);
358 }
359
postText(const char * str,long len)360 void postText(const char* str, long len) {
361 SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
362 if (client)
363 client->postFlush(str, len);
364 SC_LanguageClient::unlockInstance();
365 }
366
postChar(char c)367 void postChar(char c) {
368 SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
369 if (client)
370 client->postFlush(&c, sizeof(char));
371 SC_LanguageClient::unlockInstance();
372 }
373
error(const char * fmt,...)374 void error(const char* fmt, ...) {
375 char buf[512];
376 va_list ap;
377 va_start(ap, fmt);
378 int n = vsnprintf(buf, sizeof(buf), fmt, ap);
379 if (n > 0) {
380 SC_LanguageClient* client = SC_LanguageClient::lockedInstance();
381 if (client)
382 client->postError(buf, sc_min(n, sizeof(buf) - 1));
383 SC_LanguageClient::unlockInstance();
384 }
385 va_end(ap);
386 }
387
flushPostBuf(void)388 void flushPostBuf(void) { SC_LanguageClient::instance()->flush(); }
389
closeAllGUIScreens()390 void closeAllGUIScreens() { SC_LanguageClient::instance()->onLibraryShutdown(); }
391
initGUI()392 void initGUI() { SC_LanguageClient::instance()->onInterpStartup(); }
393
initGUIPrimitives()394 void initGUIPrimitives() { SC_LanguageClient::instance()->onLibraryStartup(); }
395
396 long scMIDIout(int port, int len, int statushi, int chan, int data1, int data2);
scMIDIout(int port,int len,int statushi,int chan,int data1,int data2)397 long scMIDIout(int port, int len, int statushi, int chan, int data1, int data2) { return 0; }
398 // EOF
399