Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Ruby_bindings/MDRubyCore.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 150 - (show annotations) (download)
Thu Jul 18 13:28:42 2019 UTC (4 years, 10 months ago) by toshinagata1964
File size: 28846 byte(s)
The binary is built for 64-bit instead of 32-bit (probably 32-bit application is no longer needed these days)
1 /*
2 * MDRubyCore.m
3 * Alchemusica
4 *
5 * Created by Toshi Nagata on 08/03/19.
6 * Copyright 2008-2017 Toshi Nagata. All rights reserved.
7 *
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation version 2 of the License.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 */
17
18 #import <Cocoa/Cocoa.h>
19 #include "MDRuby.h"
20
21 #import "MyDocument.h"
22 #import "MyMIDISequence.h"
23 #import "MDObjects.h"
24
25 #include <sys/time.h> /* for gettimeofday() */
26 #include <time.h> /* for clock() */
27 #include <signal.h> /* for sigaction() */
28
29 #include <pthread.h> /* for pthread implementation of interval timer */
30
31 #if __x86_64__
32 // Ugly hack, to avoid link error on __syscall()
33 off_t __syscall(quad_t number, ...)
34 {
35 return (off_t)(-1); // We don't need it anyway
36 }
37 #endif
38
39 // Global variables
40 int gRubyRunLevel = 0;
41 int gRubyIsCheckingInterrupt = 0;
42
43 VALUE gRubyBacktrace;
44 VALUE gRubyErrorHistory;
45
46 #pragma mark ====== Utility function ======
47
48 /*
49 * Utility function
50 * Get ary[i] by calling "[]" method
51 */
52 VALUE
53 Ruby_ObjectAtIndex(VALUE ary, int idx)
54 {
55 static ID index_method = 0;
56 if (TYPE(ary) == T_ARRAY) {
57 int len = (int)RARRAY_LEN(ary);
58 if (idx >= 0 && idx < len)
59 return (RARRAY_PTR(ary))[idx];
60 else return Qnil;
61 }
62 if (index_method == 0)
63 index_method = rb_intern("[]");
64 return rb_funcall(ary, index_method, 1, INT2NUM(idx));
65 }
66
67 char *
68 Ruby_FileStringValuePtr(VALUE *valp)
69 {
70 #if WINDOWS
71 char *p = strdup(StringValuePtr(*valp));
72 translate_char(p, '/', '����');
73 *valp = rb_str_new2(p);
74 free(p);
75 return StringValuePtr(*valp);
76 #else
77 return StringValuePtr(*valp);
78 #endif
79 }
80
81 VALUE
82 Ruby_NewFileStringValue(const char *fstr)
83 {
84 #if WINDOWS
85 VALUE retval;
86 char *p = strdup(fstr);
87 translate_char(p, '����', '/');
88 retval = rb_str_new2(p);
89 free(p);
90 return retval;
91 #else
92 return rb_str_new2(fstr);
93 #endif
94 }
95
96 char *
97 Ruby_EncodedStringValuePtr(VALUE *valp)
98 {
99 rb_string_value(valp);
100 *valp = rb_str_encode(*valp, rb_enc_from_encoding(rb_default_external_encoding()), 0, Qnil);
101 return RSTRING_PTR(*valp);
102 }
103
104 VALUE
105 Ruby_NewEncodedStringValue(const char *str, int len)
106 {
107 if (len <= 0)
108 len = (int)strlen(str);
109 return rb_enc_str_new(str, len, rb_default_external_encoding());
110 }
111
112 VALUE
113 Ruby_ObjToStringObj(VALUE val)
114 {
115 switch (TYPE(val)) {
116 case T_STRING:
117 return val;
118 case T_SYMBOL:
119 return rb_str_new2(rb_id2name(SYM2ID(val)));
120 default:
121 return rb_str_to_str(val);
122 }
123 }
124
125 void
126 Ruby_getVersionStrings(const char **version, const char **copyright)
127 {
128 static char *s_ruby_copyright;
129 *version = ruby_version;
130 if (s_ruby_copyright == NULL)
131 asprintf(&s_ruby_copyright, "Copyright (C) %d-%d %s", RUBY_BIRTH_YEAR, 2013, RUBY_AUTHOR);
132 *copyright = s_ruby_copyright;
133 }
134
135
136 #pragma mark ====== Message output ======
137
138 /*
139 * call-seq:
140 * MessageOutput.write(str)
141 *
142 * Put the message in the main text view.
143 */
144 static VALUE
145 s_MessageOutput_Write(VALUE self, VALUE str)
146 {
147 int n = MyAppCallback_showScriptMessage("%s", StringValuePtr(str));
148 return INT2NUM(n);
149 }
150
151 /*
152 * call-seq:
153 * message_box(str, title, button = nil, icon = :info)
154 *
155 * Show a message box.
156 * Buttons: nil (ok and cancel), :ok (ok only), :cancel (cancel only)
157 * Icon: :info, :warning, :error
158 */
159 static VALUE
160 s_Kernel_MessageBox(int argc, VALUE *argv, VALUE self)
161 {
162 char *str, *title, *s;
163 int buttons, icon;
164 VALUE sval, tval, bval, ival;
165 rb_scan_args(argc, argv, "22", &sval, &tval, &bval, &ival);
166 str = StringValuePtr(sval);
167 title = StringValuePtr(tval);
168 if (bval != Qnil) {
169 bval = Ruby_ObjToStringObj(bval);
170 s = RSTRING_PTR(bval);
171 if (strncmp(s, "ok", 2) == 0)
172 buttons = 1;
173 else if (strncmp(s, "cancel", 6) == 0)
174 buttons = 2;
175 else
176 rb_raise(rb_eStandardError, "the button specification should be either nil, :ok or :cancel");
177 } else buttons = 3;
178 if (ival != Qnil) {
179 ival = Ruby_ObjToStringObj(ival);
180 s = RSTRING_PTR(ival);
181 if (strncmp(s, "info", 4) == 0)
182 icon = 1;
183 else if (strncmp(s, "warn", 4) == 0)
184 icon = 2;
185 else if (strncmp(s, "err", 3) == 0)
186 icon = 3;
187 else
188 rb_raise(rb_eStandardError, "the icon specification should be either :info, :warning or :error");
189 } else icon = 1;
190 MyAppCallback_messageBox(str, title, buttons, icon);
191 return Qnil;
192 }
193
194 #pragma mark ====== Track key events ======
195
196 /* User interrupt handling
197 * User interrupt (command-period on Mac OS) is handled by periodic polling of
198 * key events. This polling should only be enabled during "normal" execution
199 * of scripts and must be disabled when the rest of the application (or Ruby
200 * script itself) is handling GUI. This is ensured by appropriate calls to
201 * enable_interrupt and disable_interrupt. */
202
203 static VALUE s_interrupt_flag = Qfalse;
204
205 static VALUE
206 s_ShowProgressPanel(int argc, VALUE *argv, VALUE self)
207 {
208 volatile VALUE message;
209 const char *p;
210 if (Ruby_GetInterruptFlag() == Qtrue) {
211 rb_scan_args(argc, argv, "01", &message);
212 if (message != Qnil)
213 p = StringValuePtr(message);
214 else
215 p = NULL;
216 MyAppCallback_showProgressPanel(p);
217 }
218 return Qnil;
219 }
220
221 static VALUE
222 s_HideProgressPanel(VALUE self)
223 {
224 MyAppCallback_hideProgressPanel();
225 return Qnil;
226 }
227
228 static VALUE
229 s_SetProgressValue(VALUE self, VALUE val)
230 {
231 double dval = NUM2DBL(rb_Float(val));
232 MyAppCallback_setProgressValue(dval);
233 return Qnil;
234 }
235
236 static VALUE
237 s_SetProgressMessage(VALUE self, VALUE msg)
238 {
239 const char *p;
240 if (msg == Qnil)
241 p = NULL;
242 else p = StringValuePtr(msg);
243 MyAppCallback_setProgressMessage(p);
244 return Qnil;
245 }
246
247 static VALUE
248 s_SetInterruptFlag(VALUE self, VALUE val)
249 {
250 VALUE oldval;
251 if (val != Qundef) {
252 if (val == Qfalse || val == Qnil)
253 val = Qfalse;
254 else val = Qtrue;
255 }
256 oldval = s_interrupt_flag;
257 if (val != Qundef) {
258 s_interrupt_flag = val;
259 if (val == Qfalse) {
260 s_HideProgressPanel(self);
261 }
262 }
263 return oldval;
264 }
265
266 static VALUE
267 s_GetInterruptFlag(VALUE self)
268 {
269 return s_SetInterruptFlag(self, Qundef);
270 }
271
272 /*
273 static VALUE
274 s_Ruby_CallMethod(VALUE val)
275 {
276 void **ptr = (void **)val;
277 VALUE receiver = (VALUE)ptr[0];
278 ID method_id = (ID)ptr[1];
279 VALUE args = (VALUE)ptr[2];
280 VALUE retval;
281 if (method_id == 0) {
282 // args should be a string, which is evaluated
283 if (receiver == Qnil) {
284 retval = rb_eval_string(StringValuePtr(args));
285 } else {
286 retval = rb_obj_instance_eval(1, &args, receiver);
287 }
288 } else {
289 // args should be an array of arguments
290 retval = rb_apply(receiver, method_id, args);
291 }
292 return retval;
293 }
294 */
295 /*
296 VALUE
297 Ruby_CallMethodWithInterrupt(VALUE receiver, ID method_id, VALUE args, int *status)
298 {
299 VALUE retval, save_interrupt_flag;
300 void *ptr[3];
301 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
302 ptr[0] = (void *)receiver;
303 ptr[1] = (void *)method_id;
304 ptr[2] = (void *)args;
305 retval = rb_protect(s_Ruby_CallMethod, (VALUE)ptr, status);
306 s_SetInterruptFlag(Qnil, save_interrupt_flag);
307 MyAppCallback_hideProgressPanel(); // In case when the progress panel is still onscreen
308 return retval;
309 }
310 */
311
312 VALUE
313 Ruby_SetInterruptFlag(VALUE val)
314 {
315 return s_SetInterruptFlag(Qnil, val);
316 }
317
318 VALUE
319 Ruby_GetInterruptFlag(void)
320 {
321 return s_SetInterruptFlag(Qnil, Qundef);
322 }
323
324 /*
325 * call-seq:
326 * check_interrupt -> integer
327 *
328 * Returns 1 if interrupted, 0 if not, -1 if interrupt is disabled.
329 */
330 static VALUE
331 s_Kernel_CheckInterrupt(VALUE self)
332 {
333 if (Ruby_GetInterruptFlag() == Qfalse)
334 return INT2NUM(-1);
335 else if (MyAppCallback_checkInterrupt())
336 return INT2NUM(1);
337 else return INT2NUM(0);
338 }
339
340 #define USE_SIGALRM 0
341
342 static volatile uint32_t sITimerCount = 0;
343
344 #if !USE_SIGALRM
345 static pthread_t sTimerThread;
346
347 /* -1: uninitiated; 0: active, 1: inactive, -2: request to terminate */
348 static volatile signed char sTimerFlag = -1;
349
350 static void *
351 s_TimerThreadEntry(void *param)
352 {
353 while (1) {
354 my_usleep(50000);
355 if (sTimerFlag == 0)
356 sITimerCount++;
357 else if (sTimerFlag == -2)
358 break;
359 }
360 return NULL;
361 }
362
363 #else
364 static void
365 s_SignalAction(int n)
366 {
367 sITimerCount++;
368 }
369 #endif
370 static void
371 s_SetIntervalTimer(int n)
372 {
373 #if !USE_SIGALRM
374 if (n == 0) {
375 if (sTimerFlag == -1) {
376 int status = pthread_create(&sTimerThread, NULL, s_TimerThreadEntry, NULL);
377 if (status != 0) {
378 fprintf(stderr, "pthread_create failed while setting Ruby interval timer: status = %d��n", status);
379 }
380 }
381 sTimerFlag = 0; /* Active */
382 } else if (sTimerFlag != -1)
383 sTimerFlag = 1; /* Inactive */
384 #else
385 static struct itimerval sOldValue;
386 static struct sigaction sOldAction;
387 struct itimerval val;
388 struct sigaction act;
389 if (n == 0) {
390 sITimerCount = 0;
391 act.sa_handler = s_SignalAction;
392 act.sa_mask = 0;
393 act.sa_flags = 0;
394 sigaction(SIGALRM, &act, &sOldAction);
395 val.it_value.tv_sec = 0;
396 val.it_value.tv_usec = 50000; /* 50 msec */
397 val.it_interval.tv_sec = 0;
398 val.it_interval.tv_usec = 50000;
399 setitimer(ITIMER_REAL, &val, &sOldValue);
400 } else {
401 setitimer(ITIMER_REAL, &sOldValue, &val);
402 sigaction(SIGALRM, &sOldAction, &act);
403 }
404 #endif
405 }
406
407 static void
408 s_Event_Callback(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
409 {
410 if (s_interrupt_flag != Qfalse) {
411 static uint32_t sLastTime = 0;
412 uint32_t currentTime;
413 int flag;
414 currentTime = sITimerCount;
415 if (currentTime != sLastTime) {
416 sLastTime = currentTime;
417 gRubyIsCheckingInterrupt = 1;
418 flag = MyAppCallback_checkInterrupt();
419 gRubyIsCheckingInterrupt = 0;
420 if (flag) {
421 s_SetInterruptFlag(Qnil, Qfalse);
422 rb_interrupt();
423 }
424 }
425 }
426 }
427
428 #pragma mark ====== Menu handling ======
429
430 /* Array of menu validators (to avoid garbage collection) */
431 static VALUE sValidatorList = Qnil;
432
433 /*
434 * call-seq:
435 * register_menu(title, method, validator = nil)
436 *
437 * Register the method (specified as a symbol) in the script menu.
438 * The method must be either an instance method of Sequence with no argument,
439 * or a class method of Sequence with one argument (the current Sequence).
440 * The menu associated with the class method can be invoked even when no document
441 * is open (the argument is set to Qnil in this case). On the other hand, the
442 * menu associated with the instance method can only be invoked when at least one
443 * document is active.
444 * Validator controls how the menu item is activated. If it is nil, then
445 * the menu is either 'always activated' (when method is a class method) or 'activated
446 * if there is an open document' (when method is an instance method). If it is a numeric
447 * 1 (one), then the menu is activated when there is an open document and at least one
448 * event is selected (in any track). Otherwise, the validator should be a callable
449 * object with one MRSequence argument, and if it returns 'true' (in Ruby sense)
450 * then the menu is activated.
451 */
452 static VALUE
453 s_Kernel_RegisterMenu(int argc, VALUE *argv, VALUE self)
454 {
455 VALUE title, method, validator;
456 rb_scan_args(argc, argv, "21", &title, &method, &validator);
457 if (TYPE(method) == T_SYMBOL) {
458 method = rb_funcall(method, rb_intern("to_s"), 0);
459 }
460 if (validator != Qnil && !FIXNUM_P(validator)) {
461 if (sValidatorList == Qnil) {
462 sValidatorList = rb_ary_new();
463 rb_define_variable("_validator_list", &sValidatorList);
464 }
465 rb_ary_push(sValidatorList, validator);
466 }
467 MyAppCallback_registerScriptMenu(StringValuePtr(method), StringValuePtr(title), (int32_t)validator);
468 return self;
469 }
470
471 static VALUE
472 s_MDRuby_methodType_sub(VALUE data)
473 {
474 const char **p = (const char **)data;
475 VALUE klass = rb_const_get(rb_cObject, rb_intern(p[0]));
476 ID mid = rb_intern(p[1]);
477 int ival;
478 if (rb_funcall(klass, rb_intern("method_defined?"), 1, ID2SYM(mid)) != Qfalse)
479 ival = 1;
480 else if (rb_respond_to(klass, mid))
481 ival = 2;
482 else ival = 0;
483 return INT2FIX(ival);
484 }
485
486 /* Returns 1 if the class defines the instance method with the given name, 2 if the class
487 has the singleton method (class method) with the given name, 0 otherwise. */
488 int
489 Ruby_methodType(const char *className, const char *methodName)
490 {
491 int status;
492 VALUE retval;
493 const char *p[2];
494 p[0] = className;
495 p[1] = methodName;
496 retval = rb_protect(s_MDRuby_methodType_sub, (VALUE)p, &status);
497 if (status == 0)
498 return FIX2INT(retval);
499 else return 0;
500 }
501
502 static VALUE
503 s_Ruby_callValidatorForDocument(VALUE data)
504 {
505 VALUE *v = (VALUE *)data;
506 MyDocument *doc = (MyDocument *)v[1];
507 if (v[0] == Qnil) {
508 /* No validator: default behavior (it is already handled by MyDocument) */
509 return Qtrue;
510 } else if (v[0] == INT2FIX(0)) {
511 /* Valid if document is open */
512 return (doc != NULL ? Qtrue : Qfalse);
513 } else if (v[0] == INT2FIX(1)) {
514 /* Valid if document is open and some events are selected */
515 int32_t n, c;
516 if (doc == NULL)
517 return Qfalse;
518 c = [[doc myMIDISequence] trackCount];
519 for (n = 0; n < c; n++) {
520 IntGroup *pset = [[doc selectionOfTrack:n] pointSet];
521 if (IntGroupGetCount(pset) > 0)
522 break;
523 }
524 return (n < c ? Qtrue : Qfalse);
525 }
526 return rb_funcall(v[0], rb_intern("call"), 1, (doc == NULL ? Qnil : MRSequenceFromMyDocument(doc)));
527 }
528
529 int
530 Ruby_callValidatorForDocument(int32_t validator, void *doc)
531 {
532 VALUE v[3];
533 VALUE retval;
534 int status;
535 v[0] = (VALUE)validator;
536 v[1] = (VALUE)doc;
537 retval = rb_protect(s_Ruby_callValidatorForDocument, (VALUE)&v, &status);
538 if (status == 0 && RTEST(retval))
539 return 1;
540 else return 0;
541 }
542
543 /*
544 * call-seq:
545 * execute_script_file(fname)
546 *
547 * Execute the script in the given file. If a molecule is active, then
548 * the script is evaluated as Molecule.current.instance_eval(script).
549 * Before entering the script, the current directory is set to the parent
550 * directory of the script.
551 */
552 static VALUE
553 s_Kernel_ExecuteScript(VALUE self, VALUE fname)
554 {
555 int status;
556 VALUE retval = (VALUE)MyAppCallback_executeScriptFromFile(StringValuePtr(fname), &status);
557 if (status != 0)
558 rb_jump_tag(status);
559 return retval;
560 }
561
562 #pragma mark ====== User defaults ======
563
564 /*
565 * call-seq:
566 * Kernel.get_global_settings(key)
567 *
568 * Get a setting data for key from the application preferences.
569 */
570 static VALUE
571 s_Kernel_GetGlobalSettings(VALUE self, VALUE key)
572 {
573 const char *p = MyAppCallback_getGlobalSettings(StringValuePtr(key));
574 if (p != NULL) {
575 return rb_str_new2(p);
576 } else return Qnil;
577 }
578
579 /*
580 * call-seq:
581 * Kernel.set_global_settings(key, value)
582 *
583 * Set a setting data for key to the application preferences.
584 */
585 static VALUE
586 s_Kernel_SetGlobalSettings(VALUE self, VALUE key, VALUE value)
587 {
588 key = rb_obj_as_string(key);
589 value = rb_obj_as_string(value);
590 MyAppCallback_setGlobalSettings(StringValuePtr(key), StringValuePtr(value));
591 return value;
592 }
593
594 /*
595 * call-seq:
596 * Kernel.sanity_check(boolean = current value)
597 *
598 * Set whether track data check is done after every editing operation
599 */
600 static VALUE
601 s_Kernel_SanityCheck(int argc, VALUE *argv, VALUE self)
602 {
603 extern int gMyDocumentSanityCheck;
604 VALUE fval = Qnil;
605 if (argc > 0)
606 fval = argv[0];
607 if (fval != Qnil) {
608 gMyDocumentSanityCheck = (RTEST(fval) ? 1 : 0);
609 }
610 return gMyDocumentSanityCheck ? Qtrue :Qfalse;
611 }
612
613
614 #pragma mark ====== Utility functions (protected funcall) ======
615
616 struct Ruby_funcall2_record {
617 VALUE recv;
618 ID mid;
619 int argc;
620 VALUE *argv;
621 };
622
623 static VALUE
624 s_Ruby_funcall2_sub(VALUE data)
625 {
626 struct Ruby_funcall2_record *rp = (struct Ruby_funcall2_record *)data;
627 return rb_funcall2(rp->recv, rp->mid, rp->argc, rp->argv);
628 }
629
630 VALUE
631 Ruby_funcall2_protect(VALUE recv, ID mid, int argc, VALUE *argv, int *status)
632 {
633 struct Ruby_funcall2_record rec;
634 rec.recv = recv;
635 rec.mid = mid;
636 rec.argc = argc;
637 rec.argv = argv;
638 return rb_protect(s_Ruby_funcall2_sub, (VALUE)&rec, status);
639 }
640
641 #pragma mark ====== Initialize class ======
642
643 void
644 MRCoreInitClass(void)
645 {
646 /* module Kernel */
647 rb_define_method(rb_mKernel, "check_interrupt", s_Kernel_CheckInterrupt, 0);
648 rb_define_method(rb_mKernel, "get_interrupt_flag", s_GetInterruptFlag, 0);
649 rb_define_method(rb_mKernel, "set_interrupt_flag", s_SetInterruptFlag, 1);
650 rb_define_method(rb_mKernel, "show_progress_panel", s_ShowProgressPanel, -1);
651 rb_define_method(rb_mKernel, "hide_progress_panel", s_HideProgressPanel, 0);
652 rb_define_method(rb_mKernel, "set_progress_value", s_SetProgressValue, 1);
653 rb_define_method(rb_mKernel, "set_progress_message", s_SetProgressMessage, 1);
654 rb_define_method(rb_mKernel, "register_menu", s_Kernel_RegisterMenu, -1);
655 rb_define_method(rb_mKernel, "get_global_settings", s_Kernel_GetGlobalSettings, 1);
656 rb_define_method(rb_mKernel, "set_global_settings", s_Kernel_SetGlobalSettings, 2);
657 rb_define_method(rb_mKernel, "execute_script", s_Kernel_ExecuteScript, 1);
658 rb_define_method(rb_mKernel, "sanity_check", s_Kernel_SanityCheck, -1);
659 rb_define_method(rb_mKernel, "message_box", s_Kernel_MessageBox, -1);
660 }
661
662 #pragma mark ====== External functions ======
663
664 typedef struct RubyArgsRecord {
665 void *doc; /* A pointer to document */
666 const char *script; /* A method name or a script */
667 int type; /* 0: script, 1: ordinary method, 2: singleton method */
668 const char *argfmt; /* Argument format string */
669 const char *fname; /* Not used yet */
670 va_list ap; /* Argument list */
671 } RubyArgsRecord;
672
673 static VALUE s_ruby_top_self = Qfalse;
674 static VALUE s_ruby_get_binding_for_document = Qfalse;
675 static VALUE s_ruby_export_local_variables = Qfalse;
676
677 static VALUE
678 s_evalRubyScriptOnDocumentSub(VALUE val)
679 {
680 RubyArgsRecord *arec = (RubyArgsRecord *)val;
681 VALUE sval, fnval, lnval, retval;
682 VALUE binding;
683
684 /* Clear the error information (store in the history array if necessary) */
685 sval = rb_errinfo();
686 if (sval != Qnil) {
687 rb_eval_string("$error_history.push([$!.to_s, $!.backtrace])");
688 rb_set_errinfo(Qnil);
689 }
690
691 if (s_ruby_top_self == Qfalse) {
692 s_ruby_top_self = rb_eval_string("eval(\"self\",TOPLEVEL_BINDING)");
693 }
694 if (s_ruby_get_binding_for_document == Qfalse) {
695 const char *s1 =
696 "lambda { |_doc_, _bind_| \n"
697 " _proc_ = eval(\"lambda { |__doc__| __doc__.instance_eval { binding } } \", _bind_) \n"
698 " _proc_.call(_doc_) } ";
699 s_ruby_get_binding_for_document = rb_eval_string(s1);
700 rb_define_variable("_get_binding_for_document", &s_ruby_get_binding_for_document);
701 }
702 if (s_ruby_export_local_variables == Qfalse) {
703 const char *s2 =
704 "lambda { |_bind_| \n"
705 " # find local variables newly defined in _bind_ \n"
706 " _a_ = _bind_.eval(\"local_variables\") - TOPLEVEL_BINDING.eval(\"local_variables\"); \n"
707 " _a_.each { |_vsym_| \n"
708 " _vname_ = _vsym_.to_s \n"
709 " _vval_ = _bind_.eval(_vname_) \n"
710 " # Define local variable \n"
711 " TOPLEVEL_BINDING.eval(_vname_ + \" = nil\") \n"
712 " # Then set value \n"
713 " TOPLEVEL_BINDING.eval(\"lambda { |_m_| \" + _vname_ + \" = _m_ }\").call(_vval_) \n"
714 " } \n"
715 "}";
716 s_ruby_export_local_variables = rb_eval_string(s2);
717 rb_define_variable("_export_local_variables", &s_ruby_export_local_variables);
718 }
719 if (arec->fname == NULL) {
720 char *scr;
721 /* String literal: we need to specify string encoding */
722 asprintf(&scr, "#coding:utf-8\n%s", arec->script);
723 sval = rb_str_new2(scr);
724 free(scr);
725 fnval = rb_str_new2("(eval)");
726 lnval = INT2FIX(0);
727 } else {
728 sval = rb_str_new2(arec->script);
729 fnval = Ruby_NewFileStringValue(arec->fname);
730 lnval = INT2FIX(1);
731 }
732 binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
733 if (arec->doc != NULL) {
734 VALUE mval = MRSequenceFromMyDocument(arec->doc);
735 binding = rb_funcall(s_ruby_get_binding_for_document, rb_intern("call"), 2, mval, binding);
736 }
737 retval = rb_funcall(binding, rb_intern("eval"), 3, sval, fnval, lnval);
738 if (arec->doc != NULL) {
739 rb_funcall(s_ruby_export_local_variables, rb_intern("call"), 1, binding);
740 }
741 return retval;
742 }
743
744 RubyValue
745 Ruby_evalRubyScriptOnDocument(const char *script, void *doc, int *status)
746 {
747 RubyValue retval;
748 RubyArgsRecord rec;
749 VALUE save_interrupt_flag;
750 /* char *save_ruby_sourcefile;
751 int save_ruby_sourceline; */
752 if (gRubyIsCheckingInterrupt) {
753 // TODO: Show alert
754 // MolActionAlertRubyIsRunning();
755 *status = -1;
756 return (RubyValue)Qnil;
757 }
758 gRubyRunLevel++;
759 memset(&rec, 0, sizeof(rec));
760 rec.doc = doc;
761 rec.script = script;
762 rec.type = 0;
763 // TODO: support fname?
764 // args[2] = (void *)fname;
765 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
766 retval = (RubyValue)rb_protect(s_evalRubyScriptOnDocumentSub, (VALUE)&rec, status);
767 if (*status != 0) {
768 /* Is this 'exit' exception? */
769 VALUE last_exception = rb_gv_get("$!");
770 if (rb_obj_is_kind_of(last_exception, rb_eSystemExit)) {
771 /* Capture exit and return the status value */
772 retval = (RubyValue)rb_funcall(last_exception, rb_intern("status"), 0);
773 *status = 0;
774 rb_set_errinfo(Qnil);
775 }
776 }
777 s_SetInterruptFlag(Qnil, save_interrupt_flag);
778 gRubyRunLevel--;
779 return retval;
780 }
781
782 static VALUE
783 s_executeRubyOnDocument(VALUE vinfo)
784 {
785 VALUE retval, args, aval, mval;
786 int i, n;
787 ID mid;
788 char retfmt = 0;
789 void *retp1, *retp2, *retp3;
790 RubyArgsRecord *rp = (RubyArgsRecord *)vinfo;
791 VALUE save_interrupt_flag;
792
793 if (rp->type == 0) {
794 /* Evaluate as string (no other arguments) */
795 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
796 if (rp->doc == NULL)
797 retval = rb_eval_string(rp->script);
798 else {
799 aval = rb_str_new2(rp->script);
800 mval = MRSequenceFromMyDocument(rp->doc);
801 retval = rb_obj_instance_eval(1, &aval, mval);
802 }
803 s_SetInterruptFlag(Qnil, save_interrupt_flag);
804 return retval;
805 }
806
807 mval = MRSequenceFromMyDocument(rp->doc);
808 mid = rb_intern(rp->script);
809 args = rb_ary_new();
810 save_interrupt_flag = s_SetInterruptFlag(Qnil, Qtrue);
811
812 /* Analyse the argfmt */
813 if (rp->argfmt != NULL && rp->argfmt[0] != 0) {
814 const char *p = rp->argfmt;
815 int ival;
816 /* va_list ap = rp->ap; *//* This assignment does not work on 64-bit build */
817 while (*p != 0) {
818 switch (*p) {
819 case 'b':
820 ival = va_arg(rp->ap, int);
821 aval = (ival ? Qtrue : Qfalse);
822 break;
823 case 'i':
824 aval = INT2NUM(va_arg(rp->ap, int)); break;
825 case 'l':
826 aval = INT2NUM(va_arg(rp->ap, int32_t)); break;
827 case 'q':
828 aval = LL2NUM(va_arg(rp->ap, int64_t)); break;
829 case 'd':
830 aval = rb_float_new(va_arg(rp->ap, double)); break;
831 case 's':
832 aval = rb_str_new2(va_arg(rp->ap, const char *)); break;
833 case 'a': /* ASCII characters (can contain NUL) */
834 n = va_arg(rp->ap, int);
835 aval = rb_str_new(va_arg(rp->ap, const char *), n);
836 break;
837 case 'B': case 'I': case 'L': case 'Q': case 'D': case 'S': case 'A': {
838 VALUE aaval;
839 void *pp;
840 n = va_arg(rp->ap, int);
841 pp = va_arg(rp->ap, void *);
842 aval = rb_ary_new2(n);
843 for (i = 0; i < n; i++) {
844 switch (*p) {
845 case 'B':
846 ival = ((int *)pp)[i];
847 aaval = (ival ? Qtrue : Qfalse);
848 break;
849 case 'I':
850 aaval = INT2NUM(((int *)pp)[i]); break;
851 case 'L':
852 aaval = INT2NUM(((int32_t *)pp)[i]); break;
853 case 'Q':
854 aaval = LL2NUM(((int64_t *)pp)[i]); break;
855 case 'D':
856 aaval = rb_float_new(((double *)pp)[i]); break;
857 case 'S':
858 aaval = rb_str_new2(((const char **)pp)[i]); break;
859 }
860 rb_ary_push(aval, aaval);
861 }
862 break;
863 }
864 case ';':
865 retfmt = *++p;
866 retp1 = va_arg(rp->ap, void *);
867 if (retfmt == 'a' || (retfmt >= 'A' && retfmt <= 'Z'))
868 retp2 = va_arg(rp->ap, void *);
869 goto out_of_loop;
870 } /* end switch */
871 rb_ary_push(args, aval);
872 p++;
873 } /* end while */
874 } /* end if */
875
876 out_of_loop:
877 if (rp->type == 2) {
878 rb_ary_unshift(args, mval);
879 retval = rb_apply(rb_cMRSequence, mid, args);
880 } else {
881 retval = rb_apply(mval, mid, args);
882 }
883 switch (retfmt) {
884 case 'b':
885 *((int *)retp1) = RTEST(retval); break;
886 case 'i':
887 *((int *)retp1) = NUM2INT(rb_Integer(retval)); break;
888 case 'l':
889 *((int32_t *)retp1) = NUM2INT(rb_Integer(retval)); break;
890 case 'q':
891 *((int64_t *)retp1) = NUM2LL(rb_Integer(retval)); break;
892 case 'd':
893 *((double *)retp1) = NUM2DBL(rb_Float(retval)); break;
894 case 's':
895 *((char **)retp1) = strdup(StringValuePtr(retval)); break;
896 case 'a':
897 retval = rb_str_to_str(retval);
898 n = (int)RSTRING_LEN(retval);
899 *((int *)retp1) = n;
900 retp3 = malloc(n + 1);
901 memmove(retp3, RSTRING_PTR(retval), n);
902 ((char *)retp3)[n] = 0;
903 *((void **)retp2) = retp3;
904 break;
905 case 'B': case 'I': case 'L': case 'Q': case 'D': case 'S':
906 switch (retfmt) {
907 case 'B': case 'I': i = sizeof(int); break;
908 case 'L': i = sizeof(int32_t); break;
909 case 'Q': i = sizeof(int64_t); break;
910 case 'D': i = sizeof(double); break;
911 case 'S': i = sizeof(char *); break;
912 }
913 if (retval == Qnil)
914 n = 0;
915 else {
916 retval = rb_ary_to_ary(retval);
917 n = (int)RARRAY_LEN(retval);
918 }
919 *((int *)retp1) = n;
920 if (n == 0)
921 retp3 = NULL;
922 else {
923 retp3 = calloc(n, i);
924 for (i = 0; i < n; i++) {
925 aval = RARRAY_PTR(retval)[i];
926 switch (retfmt) {
927 case 'B': ((int *)retp3)[i] = RTEST(aval); break;
928 case 'I': ((int *)retp3)[i] = NUM2INT(rb_Integer(aval)); break;
929 case 'L': ((int32_t *)retp3)[i] = NUM2INT(rb_Integer(aval)); break;
930 case 'Q': ((int64_t *)retp3)[i] = NUM2LL(rb_Integer(aval)); break;
931 case 'D': ((double *)retp3)[i] = NUM2DBL(rb_Float(aval)); break;
932 case 'S': ((char **)retp3)[i] = strdup(StringValuePtr(aval)); break;
933 }
934 }
935 }
936 *((void **)retp2) = retp3;
937 break;
938 }
939 s_SetInterruptFlag(Qnil, save_interrupt_flag);
940 return retval;
941 }
942
943 /* argfmt: characters representing the arguments
944 b: boolean (int), i: integer, l: int32_t integer, q: int64_t integer,
945 d: double, s: string (const char *),
946 B, I, L, Q, D, S: array of the above type (two arguments: number of values followed by a pointer)
947 ;X (X is one of the above types): return value; if the type is a simple type (represented by
948 a lowercase character), one pointer (TYPE *) is required. If the type is an array type,
949 two pointers (int * and TYPE **) are required. On returning a string or an array, the
950 required storage are allocated by malloc() (i.e. the caller is responsible for free'ing them).
951 If the return value is an array of strings, each string should be free'd. */
952 int
953 Ruby_callMethodOfDocument(const char *name, void *document, int isSingleton, const char *argfmt, ...)
954 {
955 RubyArgsRecord rec;
956 int status;
957 rec.doc = document;
958 rec.script = name;
959 rec.type = isSingleton + 1;
960 rec.argfmt = argfmt;
961 va_start(rec.ap, argfmt);
962 rb_protect(s_executeRubyOnDocument, (VALUE)&rec, &status);
963 return status;
964 }
965
966 int
967 Ruby_showValue(RubyValue value, char **outValueString)
968 {
969 VALUE val = (VALUE)value;
970 if (gRubyIsCheckingInterrupt) {
971 // TODO: show error message
972 return 0;
973 }
974 if (val != Qnil) {
975 int status;
976 char *str;
977 gRubyRunLevel++;
978 val = rb_protect(rb_inspect, val, &status);
979 gRubyRunLevel--;
980 if (status != 0)
981 return status;
982 str = StringValuePtr(val);
983 if (outValueString != NULL)
984 *outValueString = strdup(str);
985 MyAppCallback_showScriptMessage("%s", str);
986 } else {
987 if (outValueString != NULL)
988 *outValueString = NULL;
989 }
990 return 0;
991 }
992
993 void
994 Ruby_showError(int status)
995 {
996 static const int tag_raise = 6;
997 char *msg = NULL, *msg2;
998 VALUE val, backtrace;
999 int interrupted = 0;
1000 if (status == tag_raise) {
1001 VALUE errinfo = rb_errinfo();
1002 VALUE eclass = CLASS_OF(errinfo);
1003 if (eclass == rb_eInterrupt) {
1004 msg = "Interrupt";
1005 interrupted = 1;
1006 }
1007 }
1008 gRubyRunLevel++;
1009 backtrace = rb_eval_string_protect("$backtrace = $!.backtrace.join(\"\\n\")", &status);
1010 if (msg == NULL) {
1011 val = rb_eval_string_protect("$!.to_s", &status);
1012 if (status == 0)
1013 msg = RSTRING_PTR(val);
1014 else msg = "(message not available)";
1015 }
1016 asprintf(&msg2, "%s\n%s", msg, RSTRING_PTR(backtrace));
1017 MyAppCallback_messageBox(msg2, (interrupted == 0 ? "Ruby script error" : "Ruby script interrupted"), 0, 3);
1018 free(msg2);
1019 gRubyRunLevel--;
1020 }
1021
1022 void
1023 Ruby_startup(void)
1024 {
1025 VALUE val;
1026
1027 /* Initialize Ruby interpreter */
1028 ruby_init();
1029 ruby_init_loadpath();
1030 ruby_script("MDRuby");
1031
1032 /* Define MDRuby classes */
1033 MREventSetInitClass();
1034 MRCoreInitClass();
1035 MRSequenceInitClass();
1036 MRTrackInitClass();
1037 MRPointerInitClass();
1038 RubyDialogInitClass();
1039
1040 /* Create an object for standard output and standard error */
1041 val = rb_obj_alloc(rb_cObject);
1042 rb_define_singleton_method(val, "write", s_MessageOutput_Write, 1);
1043 rb_gv_set("$stdout", val);
1044 rb_gv_set("$stderr", val);
1045
1046 /* Global variable to hold error information */
1047 rb_define_variable("$backtrace", &gRubyBacktrace);
1048 rb_define_variable("$error_history", &gRubyErrorHistory);
1049 gRubyErrorHistory = rb_ary_new();
1050 gRubyBacktrace = Qnil;
1051
1052 /* Register interrupt check code */
1053 rb_add_event_hook(s_Event_Callback, RUBY_EVENT_ALL, Qnil);
1054
1055 /* Start interval timer */
1056 s_SetIntervalTimer(0);
1057 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26