]> icculus.org git repositories - duncan/yast2-ruby-bindings.git/blob - src/ruby/YCP.cc
3ffb58b1378adf2157ca6dc5ed905d96619a1084
[duncan/yast2-ruby-bindings.git] / src / ruby / YCP.cc
1 /*---------------------------------------------------------------------\
2 |                                                                      |
3 |                      __   __    ____ _____ ____                      |
4 |                      \ \ / /_ _/ ___|_   _|___ \                     |
5 |                       \ V / _` \___ \ | |   __) |                    |
6 |                        | | (_| |___) || |  / __/                     |
7 |                        |_|\__,_|____/ |_| |_____|                    |
8 |                                                                      |
9 |                                                                      |
10 | ruby language support                              (C) Novell Inc.   |
11 \----------------------------------------------------------------------/
12
13 Author: Duncan Mac-Vicar <dmacvicar@suse.de>
14
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version
18 2 of the License, or (at your option) any later version.
19
20 */
21
22 // #include <y2/Y2Namespace.h>
23 // #include <y2/Y2Component.h>
24 #include <y2/Y2ComponentCreator.h>
25
26 #include <yui/YUIComponent.h>
27 #include <wfm/Y2WFMComponent.h>
28
29 #include <y2/Y2ComponentBroker.h>
30 #include <y2/Y2Namespace.h>
31 #include <y2/Y2Component.h>
32 #include <y2/Y2Function.h>
33
34 #include <ycp/pathsearch.h>
35 #include <ycp/y2log.h>
36 #include <ycp/YBlock.h>
37 #include <ycp/YExpression.h>
38 #include <ycp/YStatement.h>
39 #include <ycp/YCPValue.h>
40 #include <ycp/YCPBoolean.h>
41 #include <ycp/YCPList.h>
42 #include <ycp/YCPMap.h>
43 #include <ycp/YCPString.h>
44 #include <ycp/YCPInteger.h>
45 #include <ycp/YCPFloat.h>
46 #include <ycp/YCPElement.h>
47 #include <ycp/Import.h>
48 #include <ycp/y2log.h>
49
50 #include "ruby.h"
51
52 //#include "YRuby.h"
53 #include "RubyLogger.h"
54 #include "Y2RubyTypePath.h"
55 #include "Y2RubyTypeTerm.h"
56 #include "Y2RubyTypeConv.h"
57 #include "YRuby.h"
58
59 #define GetY2Object(obj, po) \
60     Data_Get_Struct(obj, Y2Namespace, po)
61
62 static VALUE rb_mYaST;
63 static VALUE rb_mY2ModuleProxy;
64 static VALUE rb_mUi;
65 static VALUE rb_mYCP;
66 static VALUE rb_cBroker;
67
68 // make the compiler happy when
69 // calling rb_define_method()
70 typedef VALUE (ruby_method)(...);
71
72 // more useful macros
73 #define RB_FINALIZER(func) ((void (*)(...))func)
74
75 // this macro saves us from typing
76 // (ruby_method*) & method_name
77 // in rb_define_method
78 #define RB_METHOD(func) ((VALUE (*)(...))func)
79
80 Y2Component *owned_uic = 0;
81 Y2Component *owned_wfmc = 0;
82
83 static
84 Y2Namespace *
85 getNs (const char * ns_name)
86 {
87   Import import(ns_name);  // has a static cache
88   Y2Namespace *ns = import.nameSpace();
89   if (ns == NULL)
90   {
91     y2error ("Ruby call: Can't import namespace '%s'", ns_name);
92   }
93   else
94   {
95     ns->initialize ();
96   }
97   return ns;
98 }
99
100 void init_wfm()
101 {
102     y2milestone("init_wfm");
103 //     if (Y2WFMComponent::instance () == 0)
104 //     {
105       y2milestone("WFM init");
106       owned_wfmc = Y2ComponentBroker::createClient ("wfm");
107       if (owned_wfmc == 0)
108       {
109           y2error ("Cannot create WFM component");
110       }
111 //     }
112 }
113
114 static VALUE
115 rb_init_ui( int argc, VALUE *argv, VALUE self )
116 {
117   const char *ui_name = "ncurses";
118
119   if (argc == 1)
120   {
121     Check_Type(argv[0], T_STRING);
122     ui_name = RSTRING(argv[0])->ptr;
123   }
124   else if (argc != 0)
125   {
126     y2error ("Zero or one arguments required (ui name, default %s", ui_name);
127     return Qnil;
128   }
129
130   Y2Component *c = YUIComponent::uiComponent ();
131   if (c == 0)
132   {
133     y2debug ("UI component not created yet, creating %s", ui_name);
134
135     c = Y2ComponentBroker::createServer (ui_name);
136     if (c == 0)
137     {
138       y2error ("Cannot create component %s", ui_name);
139       return Qnil;
140     }
141
142     if (YUIComponent::uiComponent () == 0)
143     {
144       y2error ("Component %s is not a UI", ui_name);
145       return Qnil;
146     }
147     else
148     {
149       // got it - initialize, remember
150       c->setServerOptions (0, NULL);
151       owned_uic = c;
152     }
153   }
154   else
155   {
156     y2debug ("UI component already present: %s", c->name ().c_str ());
157   }
158   return Qnil;
159 }
160
161 typedef struct brokerinfo
162 {
163   Y2Component *component;
164   Y2Namespace *ns;
165 }
166 BrokerInfo;
167
168 //  mark for garbage collection
169 // NOT USED, ONLY WHEN USING DATA_WRAP_STRUCT
170 void
171 yast_module_mark( BrokerInfo *info )
172 {}
173
174 // dispose a namespace
175 // NOT USED, ONLY WHEN USING DATA_WRAP_STRUCT
176 void
177 yast_module_free( BrokerInfo *info )
178 {
179   //std::cout << "Deleting namespace." << std::endl;
180   //delete info;
181 }
182
183 VALUE
184 yast_module_allocate( VALUE klass )
185 {
186   BrokerInfo *info = new BrokerInfo;
187   return Data_Wrap_Struct( klass, yast_module_mark, RB_FINALIZER(yast_module_free), info );
188 }
189
190 VALUE
191 yast_module_initialize( VALUE self, VALUE param_ns_name )
192 {
193   Check_Type(param_ns_name, T_STRING);
194   rb_iv_set(self, "@namespace_name", param_ns_name);
195   init_wfm();
196   return self;
197 }
198
199
200 VALUE
201 yast_module_name( VALUE self )
202 {
203   return rb_iv_get(self, "@namespace_name");
204 }
205
206 VALUE
207 yast_module_methods( VALUE self )
208 {
209   return Qnil;
210 }
211
212 //forward declaration
213 YCPValue ycp_call_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv );
214
215 /**
216  * looks a component for a namespace
217  * throws
218  */
219 static VALUE
220 ycp_module_lookup_namespace_component(VALUE self, VALUE name)
221 {
222   Y2Component *c;
223   c = Y2ComponentBroker::getNamespaceComponent(RSTRING(name)->ptr);
224   if (c == NULL)
225   {
226     y2internal("No component can provide namespace %s\n", RSTRING (name)->ptr);
227     rb_raise( rb_eRuntimeError, "No YaST component can provide namespace %s\n", RSTRING (name)->ptr);
228   }
229   y2internal("component name %s\n", c->name().c_str());
230   return Qtrue;
231 }
232
233 /*
234  tries to import a namespace and throws a NameError if
235  failed
236 */
237 static VALUE
238 ycp_module_import_namespace( VALUE self, VALUE namespace_name)
239 {
240   Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr);
241   if (ns == NULL)
242   {
243     rb_raise( rb_eNameError, "Component cannot import namespace '%s'", RSTRING (namespace_name)->ptr);
244     return Qnil;
245   }
246   else
247   {
248     y2internal("Namespace created from %s\n", ns->filename().c_str());
249   }
250   return Qtrue;
251 }
252
253 static VALUE
254 ycp_module_import( VALUE self, VALUE name)
255 {
256   ycp_module_lookup_namespace_component(self,name);
257   ycp_module_import_namespace(self,name);
258 }
259
260 /**
261  * iterates all symbols in a namespace and yields the
262  * symbol name and category
263  */
264 VALUE
265 ycp_module_each_symbol(VALUE self, VALUE namespace_name)
266 {
267   Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr);
268   if (ns == NULL)
269   {
270     rb_raise( rb_eRuntimeError, "Error getting namespace '%s'", RSTRING (namespace_name)->ptr );
271     return Qnil;
272   }
273   else
274   {
275     y2internal("Got namespace from %s\n", ns->filename().c_str());
276   }
277
278   for (int i=0; i < ns->symbolCount(); ++i)
279   {
280     SymbolEntryPtr s = ns->symbolEntry(i);
281     VALUE arr = rb_ary_new();
282     rb_ary_push(arr, rb_str_new2(s->name()));
283     rb_ary_push(arr, ID2SYM(rb_intern(s->catString().c_str())));
284     rb_yield(arr);
285   }
286   return Qnil;
287 }
288
289 /**
290  * Forwards a ruby call to the namespace
291  * First argument is the namespace
292  * then function name and arguments
293  */
294 VALUE
295 ycp_module_forward_call(int argc, VALUE *argv, VALUE self)
296 {
297   VALUE symbol = argv[1];
298   VALUE namespace_name = argv[0];
299
300   // the func name (1st argument, is a symbol
301   // lets convert it to string
302   VALUE symbol_str = rb_funcall(symbol, rb_intern("to_s"), 0);
303   y2internal("Dynamic Proxy: [%s::%s] with [%d] params\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc);
304
305   //Check_Type(argv[0], T_STRING);
306   //y2internal(RSTRING (symbol)->ptr);
307   //Data_Get_Struct( self, class Y2Namespace, ns );
308   //ns = gNameSpaces[self];
309
310   // get the name of the module
311   //VALUE namespace_name = rb_funcall(self, rb_intern("name"), 0);
312   
313
314   // SCR
315   // this is a hack before the builtin namespaces get a uniform interface:
316   if (! strcmp (RSTRING (namespace_name)->ptr, "SCR"))
317   {
318     y2internal("Dynamic Proxy: [%s::%s] is a call to SCR\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr);
319     YCPValue res;
320     res = ycp_call_builtin( RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc, argv);
321     return ycpvalue_2_rbvalue(res);
322     //return Qnil;
323   }
324
325   ycp_module_lookup_namespace_component(self, namespace_name);
326
327   // import the namespace
328   //Y2Namespace *ns = c->import(RSTRING (namespace_name)->ptr);
329   Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr);
330   if (ns == NULL)
331   {
332     rb_raise( rb_eRuntimeError, "Component cannot import namespace '%s' for symbol '%s'", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr );
333     return Qnil;
334   }
335   else
336   {
337     y2internal("Namespace created from %s\n", ns->filename().c_str());
338   }
339
340   y2internal("Namespace %s initialized\n", RSTRING (namespace_name)->ptr);
341
342   TableEntry *sym_te = ns->table()->find (RSTRING(symbol_str)->ptr);
343
344   if (sym_te == NULL)
345   {
346     y2error ("No such symbol %s::%s", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
347     rb_raise( rb_eNameError, "YCP symbol '%s' not found in namespace '%s'", RSTRING(symbol_str)->ptr, RSTRING (namespace_name)->ptr );
348     return Qnil;
349   }
350
351   if (sym_te->sentry ()->isVariable () ||
352       sym_te->sentry ()->isReference ())
353   {
354     y2internal ("Variable or reference %s\n", RSTRING(symbol_str)->ptr);
355     // set the variable
356     //ret_yv = YCP_getset_variable (aTHX_ ns_name, sym_te->sentry (), args);
357   }
358   else
359   { // no indent yet
360     Y2Function* call = ns->createFunctionCall (RSTRING(symbol_str)->ptr, 0 /*Type::fromSignature("list<string>()")*/);
361
362     if (call == NULL)
363     {
364       y2internal ("Cannot create function call %s\n", RSTRING(symbol_str)->ptr);
365       rb_raise( rb_eRuntimeError, "Can't create call to %s::%s", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
366     }
367
368     // add the parameters
369     for (int i=2; i < argc; i++)
370     {
371       VALUE arg = argv[i];
372       y2internal ("Appending parameter #%d\n", i);
373       YCPValue v = rbvalue_2_ycpvalue(arg);
374       y2internal ("Appending parameter #%s : %s\n", i, v->toString());
375       call->appendParameter (v);
376     }
377     call->finishParameters ();
378
379     YCPValue res = call->evaluateCall ();
380     delete call;
381     y2internal ("Call succeded\n");
382     //y2internal ("Result: %i\n", res->asList()->size());
383     return ycpvalue_2_rbvalue(res);
384   }
385   return Qnil;
386 }
387
388 YCPValue ycp_call_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv )
389 {
390   // access directly the statically declared builtins
391   extern StaticDeclaration static_declarations;
392   string qualified_name_s = module_name + "::" + func_name;
393   const char *qualified_name = qualified_name_s.c_str ();
394
395   y2milestone("Qualified name '%s'", qualified_name);
396   /*
397     this does not work across namespaces
398       TableEntry *bi_te = static_declarations.symbolTable ()->find (qualified_name);
399       if (bi_te == NULL)
400       {
401     y2error ("No such builtin %s",qualified_name);
402     return YCPNull ();
403       }
404   
405       SymbolEntry *bi_se = bi_te->sentry ();
406       assert (bi_se != NULL);
407       assert (bi_se->isBuiltin ());
408       declaration_t bi_dt = bi_se->declaration ();
409   */
410   declaration_t *bi_dt = static_declarations.findDeclaration (qualified_name);
411   if (bi_dt == NULL)
412   {
413     y2error ("No such builtin '%s'", qualified_name);
414     return YCPNull ();
415   }
416   y2milestone("builtin '%s' found.", module_name.c_str());
417   // construct a builtin call using the proper overloaded builtin
418   YEBuiltin *bi_call = new YEBuiltin (bi_dt);
419
420   // attach the parameters:
421
422   // we would like to know the destination type so that we could
423   // convert eg a Ruby scalar to a YCP symbol, but because the
424   // builtins may be overloaded, let's say we want Any
425
426   // maybe a special exceptional hack to make Path for the 1st argument?
427
428   // go through the actual parameters
429   int j;
430   for (j = 1; j < argc; ++j)
431   {
432     y2milestone("builtin param '%d'", j-1);
433     // convert the value according to the expected type:
434     constTypePtr param_tp = (j == 0)? Type::Path : Type::Any;
435     y2milestone("builtin param '%d' 1", j-1);
436     
437     // convert the first argument to a path
438     YCPValue param_v;
439     if ( j == 1 )
440       param_v = rbvalue_2_ycppath(argv[j]);
441     else
442       param_v = rbvalue_2_ycpvalue(argv[j] /*, param_tp */);
443     
444     y2milestone("builtin param '%d' 2", j-1);
445     if (param_v.isNull ())
446     {
447       // an error has already been reported, now refine it.
448       // Can't know parameter name?
449       y2error ("... when passing parameter #%u to builtin %s",
450         j, qualified_name);
451       return YCPNull ();
452     }
453     // Such YConsts without a specific type produce invalid
454     // bytecode. (Which is OK here)
455     // The actual parameter's YCode becomes owned by the function call?
456     YConst *param_c = new YConst (YCode::ycConstant, param_v);
457     // for attaching the parameter, must get the real type so that it matches
458     constTypePtr act_param_tp = Type::vt2type (param_v->valuetype ());
459     // Attach the parameter
460     // Returns NULL if OK, Type::Error if excessive argument
461     // Other errors (bad code, bad type) shouldn't happen
462     constTypePtr err_tp = bi_call->attachParameter (param_c, act_param_tp);
463     if (err_tp != NULL)
464     {
465         if (err_tp->isError ())
466         {
467           // TODO really need to know the place in Ruby code
468           // where we were called from.
469           y2error ("Excessive parameter to builtin %s", qualified_name);
470         }
471         else
472         {
473           y2internal ("attachParameter returned %s", err_tp->toString ().c_str ());
474         }
475         return YCPNull ();
476     }
477   } // for each actual parameter
478
479   // now must check if we got fewer parameters than needed
480   // or there was another error while resolving the overload
481   constTypePtr err_tp = bi_call->finalize (RubyLogger::instance ());
482   if (err_tp != NULL)
483   {
484     // apparently the error was already reported?
485     y2error ("Error type %s when finalizing builtin %s",
486     err_tp->toString ().c_str (), qualified_name);
487     return YCPNull ();
488   }
489
490   // go call it now!
491   y2debug ("Ruby is calling builtin %s", qualified_name);
492   YCPValue ret_yv = bi_call->evaluate (false /* no const subexpr elim */);
493   delete bi_call;
494
495   return ret_yv;
496 }
497
498
499
500 //y2_logger_helper
501
502 //y2_logger (level, comp, file, line, function, "%s", message);
503
504 static VALUE
505 rb_y2_logger( int argc, VALUE *argv, VALUE self )
506 {
507   Check_Type(argv[0], T_FIXNUM);
508   Check_Type(argv[1], T_STRING);
509   Check_Type(argv[2], T_STRING);
510   Check_Type(argv[3], T_FIXNUM);
511   Check_Type(argv[4], T_STRING);
512
513   int i;
514   for ( i = 5; i < argc; i++)
515   {
516     Check_Type(argv[i], T_STRING);
517   }
518   y2_logger((loglevel_t)NUM2INT(argv[0]),RSTRING(argv[1])->ptr,RSTRING(argv[2])->ptr,NUM2INT(argv[3]),"",RSTRING(argv[5])->ptr);
519   return Qnil;
520 }
521
522 extern "C"
523 {
524   void
525   Init_yastx()
526   {
527     YCPPathSearch::initialize ();
528
529     for ( list<string>::const_iterator it = YCPPathSearch::searchListBegin (YCPPathSearch::Module); it != YCPPathSearch::searchListEnd (YCPPathSearch::Module) ; ++it )
530     {
531       y2internal("%s\n", (*it).c_str() );
532     }
533
534     rb_mYaST = rb_define_module("YaST");
535
536     //rb_mYCP = rb_define_module_under(rb_mYaST, "YCP");
537     rb_mYCP = rb_define_module("YCP");
538     rb_define_singleton_method( rb_mYCP, "import", RB_METHOD(ycp_module_import), 1);
539     rb_define_singleton_method( rb_mYCP, "forward_call", RB_METHOD(ycp_module_forward_call), -1);
540     rb_define_singleton_method( rb_mYCP, "each_symbol", RB_METHOD(ycp_module_each_symbol), 1);
541     
542
543     rb_mUi = rb_define_module_under(rb_mYCP, "Ui");
544     rb_define_singleton_method( rb_mUi, "init", RB_METHOD(rb_init_ui), -1);
545     
546     
547     rb_define_method( rb_mYaST, "y2_logger", RB_METHOD(rb_y2_logger), -1);
548     
549     rb_cBroker = rb_define_class_under( rb_mYaST, "Module", rb_cObject);
550     //rb_define_singleton_method( rb_cBroker, "new", RB_METHOD(module_new), 1);
551     rb_define_alloc_func(rb_cBroker, yast_module_allocate);
552     rb_define_method(rb_cBroker, "initialize", RB_METHOD(yast_module_initialize), 1);
553     //rb_define_method( rb_cBroker, "method_missing", RB_METHOD(yast_module_proxy_method), -1);
554     rb_define_method( rb_cBroker, "name", RB_METHOD(yast_module_name), -1);
555
556     ryast_path_init(rb_mYaST);
557     ryast_term_init(rb_mYaST);
558   }
559 }