]> icculus.org git repositories - duncan/yast2-ruby-bindings.git/blob - src/ruby/YCP.cc
- Lot of fixes and support for the UI!!!
[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 static VALUE rb_mYaST;
60 static VALUE rb_mUi;
61 static VALUE rb_cBroker;
62
63 // make the compiler happy when
64 // calling rb_define_method()
65 typedef VALUE (ruby_method)(...);
66
67 // more useful macros
68 #define RB_FINALIZER(func) ((void (*)(...))func)
69
70 // this macro saves us from typing
71 // (ruby_method*) & method_name
72 // in rb_define_method
73 #define RB_METHOD(func) ((VALUE (*)(...))func)
74
75 Y2Component *owned_uic = 0;
76 Y2Component *owned_wfmc = 0;
77
78 static
79 Y2Namespace *
80 getNs (const char * ns_name, const char * func_name)
81 {
82   Import import(ns_name);  // has a static cache
83   Y2Namespace *ns = import.nameSpace();
84   if (ns == NULL)
85   {
86     y2error ("... for a Ruby call of %s", func_name);
87   }
88   else
89   {
90     ns->initialize ();
91   }
92   return ns;
93 }
94
95 void init_wfm()
96 {
97     y2milestone("init_wfm");
98 //     if (Y2WFMComponent::instance () == 0)
99 //     {
100       y2milestone("WFM init");
101       owned_wfmc = Y2ComponentBroker::createClient ("wfm");
102       if (owned_wfmc == 0)
103       {
104           y2error ("Cannot create WFM component");
105       }
106 //     }
107 }
108
109 static VALUE
110 rb_init_ui( int argc, VALUE *argv, VALUE self )
111 {
112   const char *ui_name = "ncurses";
113
114   if (argc == 1)
115   {
116     Check_Type(argv[0], T_STRING);
117     ui_name = RSTRING(argv[0])->ptr;
118   }
119   else if (argc != 0)
120   {
121     y2error ("Zero or one arguments required (ui name, default %s", ui_name);
122     return Qnil;
123   }
124
125   Y2Component *c = YUIComponent::uiComponent ();
126   if (c == 0)
127   {
128     y2debug ("UI component not created yet, creating %s", ui_name);
129
130     c = Y2ComponentBroker::createServer (ui_name);
131     if (c == 0)
132     {
133       y2error ("Cannot create component %s", ui_name);
134       return Qnil;
135     }
136
137     if (YUIComponent::uiComponent () == 0)
138     {
139       y2error ("Component %s is not a UI", ui_name);
140       return Qnil;
141     }
142     else
143     {
144       // got it - initialize, remember
145       c->setServerOptions (0, NULL);
146       owned_uic = c;
147     }
148   }
149   else
150   {
151     y2debug ("UI component already present: %s", c->name ().c_str ());
152   }
153   return Qnil;
154 }
155
156 typedef struct brokerinfo
157 {
158   Y2Component *component;
159   Y2Namespace *ns;
160 }
161 BrokerInfo;
162
163 //  mark for garbage collection
164 // NOT USED, ONLY WHEN USING DATA_WRAP_STRUCT
165 void
166 yast_module_mark( BrokerInfo *info )
167 {}
168
169 // dispose a namespace
170 // NOT USED, ONLY WHEN USING DATA_WRAP_STRUCT
171 void
172 yast_module_free( BrokerInfo *info )
173 {
174   //std::cout << "Deleting namespace." << std::endl;
175   //delete info;
176 }
177
178 VALUE
179 yast_module_allocate( VALUE klass )
180 {
181   BrokerInfo *info = new BrokerInfo;
182   return Data_Wrap_Struct( klass, yast_module_mark, RB_FINALIZER(yast_module_free), info );
183 }
184
185 VALUE
186 yast_module_initialize( VALUE self, VALUE param_ns_name )
187 {
188   Check_Type(param_ns_name, T_STRING);
189   rb_iv_set(self, "@namespace_name", param_ns_name);
190   init_wfm();
191   return self;
192 }
193
194
195 VALUE
196 yast_module_name( VALUE self )
197 {
198   return rb_iv_get(self, "@namespace_name");
199 }
200
201 VALUE
202 yast_module_methods( VALUE self )
203 {
204   return Qnil;
205 }
206
207 //forward declaration
208 YCPValue ycp_call_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv );
209
210 VALUE
211 yast_module_proxy_method( int argc, VALUE *argv, VALUE self )
212 {
213   VALUE symbol = argv[0];
214   VALUE namespace_name = rb_iv_get(self, "@namespace_name");
215
216   // the func name (1st argument, is a symbol
217   // lets convert it to string
218   VALUE symbol_str = rb_funcall(symbol, rb_intern("to_s"), 0);
219   y2internal("Dynamic Proxy: [%s::%s] with [%d] params\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc);
220
221   //Check_Type(argv[0], T_STRING);
222   //y2internal(RSTRING (symbol)->ptr);
223   //Data_Get_Struct( self, class Y2Namespace, ns );
224   //ns = gNameSpaces[self];
225
226   // get the name of the module
227   //VALUE namespace_name = rb_funcall(self, rb_intern("name"), 0);
228   
229
230   // SCR
231   // this is a hack before the builtin namespaces get a uniform interface:
232   if (! strcmp (RSTRING (namespace_name)->ptr, "SCR"))
233   {
234     y2internal("Dynamic Proxy: [%s::%s] is a call to SCR\n", RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr);
235     YCPValue res;
236     res = ycp_call_builtin( RSTRING(namespace_name)->ptr, RSTRING(symbol_str)->ptr, argc, argv);
237     return ycpvalue_2_rbvalue(res);
238     //return Qnil;
239   }
240
241   Y2Component *c;
242   c = Y2ComponentBroker::getNamespaceComponent(RSTRING (namespace_name)->ptr);
243   if (c == NULL)
244   {
245     y2internal("No component can provide namespace %s\n", RSTRING (namespace_name)->ptr);
246     rb_raise( rb_eRuntimeError, "No component can provide namespace %s\n", RSTRING (namespace_name)->ptr);
247     return 2;
248   }
249   y2internal("component name %s\n", c->name().c_str());
250
251   // import the namespace
252   //Y2Namespace *ns = c->import(RSTRING (namespace_name)->ptr);
253   Y2Namespace *ns = getNs( RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
254   if (ns == NULL)
255   {
256     rb_raise( rb_eRuntimeError, "Component cannot import namespace %s", RSTRING (namespace_name)->ptr );
257     return Qnil;
258   }
259   else
260   {
261     y2internal("Namespace created from %s\n", ns->filename().c_str());
262   }
263   // ensure it is an initialized namespace
264   //ns->initialize ();
265   y2internal("Namespace %s initialized\n", RSTRING (namespace_name)->ptr);
266
267   TableEntry *sym_te = ns->table()->find (RSTRING(symbol_str)->ptr);
268
269   if (sym_te == NULL)
270   {
271     y2error ("No such symbol %s::%s", RSTRING (namespace_name)->ptr, RSTRING(symbol_str)->ptr);
272     rb_raise( rb_eNameError, "YCP symbol '%s' not found in namespace '%s'", RSTRING(symbol_str)->ptr, RSTRING (namespace_name)->ptr );
273     return Qnil;
274   }
275
276   if (sym_te->sentry ()->isVariable () ||
277       sym_te->sentry ()->isReference ())
278   {
279     // set the variable
280     //ret_yv = YCP_getset_variable (aTHX_ ns_name, sym_te->sentry (), args);
281   }
282   else
283   { // no indent yet
284     Y2Function* call = ns->createFunctionCall (RSTRING(symbol_str)->ptr, 0 /*Type::fromSignature("list<string>()")*/);
285
286     if (call == NULL)
287     {
288       y2internal ("Cannot create function call %s\n", RSTRING(symbol_str)->ptr);
289       return 4;
290     }
291
292     // add the parameters
293     for (int i=1; i<argc; i++)
294     {
295       VALUE arg = argv[i];
296       YCPValue v = rbvalue_2_ycpvalue(arg);
297       //y2internal ("Appending parameter %s\n", v->toString());
298       call->appendParameter (v);
299     }
300     call->finishParameters ();
301
302     YCPValue res = call->evaluateCall ();
303     delete call;
304     y2internal ("Call succeded\n");
305     //y2internal ("Result: %i\n", res->asList()->size());
306     return ycpvalue_2_rbvalue(res);
307   }
308   return Qnil;
309 }
310
311 YCPValue ycp_call_builtin ( const string &module_name, const string &func_name, int argc, VALUE *argv )
312 {
313   // access directly the statically declared builtins
314   extern StaticDeclaration static_declarations;
315   string qualified_name_s = module_name + "::" + func_name;
316   const char *qualified_name = qualified_name_s.c_str ();
317
318   y2milestone("Qualified name '%s'", qualified_name);
319   /*
320     this does not work across namespaces
321       TableEntry *bi_te = static_declarations.symbolTable ()->find (qualified_name);
322       if (bi_te == NULL)
323       {
324     y2error ("No such builtin %s",qualified_name);
325     return YCPNull ();
326       }
327   
328       SymbolEntry *bi_se = bi_te->sentry ();
329       assert (bi_se != NULL);
330       assert (bi_se->isBuiltin ());
331       declaration_t bi_dt = bi_se->declaration ();
332   */
333   declaration_t *bi_dt = static_declarations.findDeclaration (qualified_name);
334   if (bi_dt == NULL)
335   {
336     y2error ("No such builtin '%s'", qualified_name);
337     return YCPNull ();
338   }
339   y2milestone("builtin '%s' found.", module_name.c_str());
340   // construct a builtin call using the proper overloaded builtin
341   YEBuiltin *bi_call = new YEBuiltin (bi_dt);
342
343   // attach the parameters:
344
345   // we would like to know the destination type so that we could
346   // convert eg a Ruby scalar to a YCP symbol, but because the
347   // builtins may be overloaded, let's say we want Any
348
349   // maybe a special exceptional hack to make Path for the 1st argument?
350
351   // go through the actual parameters
352   int j;
353   for (j = 1; j < argc; ++j)
354   {
355     y2milestone("builtin param '%d'", j-1);
356     // convert the value according to the expected type:
357     constTypePtr param_tp = (j == 0)? Type::Path : Type::Any;
358     y2milestone("builtin param '%d' 1", j-1);
359     
360     // convert the first argument to a path
361     YCPValue param_v;
362     if ( j == 1 )
363       param_v = rbvalue_2_ycppath(argv[j]);
364     else
365       param_v = rbvalue_2_ycpvalue(argv[j] /*, param_tp */);
366     
367     y2milestone("builtin param '%d' 2", j-1);
368     if (param_v.isNull ())
369     {
370       // an error has already been reported, now refine it.
371       // Can't know parameter name?
372       y2error ("... when passing parameter #%u to builtin %s",
373         j, qualified_name);
374       return YCPNull ();
375     }
376     // Such YConsts without a specific type produce invalid
377     // bytecode. (Which is OK here)
378     // The actual parameter's YCode becomes owned by the function call?
379     YConst *param_c = new YConst (YCode::ycConstant, param_v);
380     // for attaching the parameter, must get the real type so that it matches
381     constTypePtr act_param_tp = Type::vt2type (param_v->valuetype ());
382     // Attach the parameter
383     // Returns NULL if OK, Type::Error if excessive argument
384     // Other errors (bad code, bad type) shouldn't happen
385     constTypePtr err_tp = bi_call->attachParameter (param_c, act_param_tp);
386     if (err_tp != NULL)
387     {
388         if (err_tp->isError ())
389         {
390           // TODO really need to know the place in Ruby code
391           // where we were called from.
392           y2error ("Excessive parameter to builtin %s", qualified_name);
393         }
394         else
395         {
396           y2internal ("attachParameter returned %s", err_tp->toString ().c_str ());
397         }
398         return YCPNull ();
399     }
400   } // for each actual parameter
401
402   // now must check if we got fewer parameters than needed
403   // or there was another error while resolving the overload
404   constTypePtr err_tp = bi_call->finalize (RubyLogger::instance ());
405   if (err_tp != NULL)
406   {
407     // apparently the error was already reported?
408     y2error ("Error type %s when finalizing builtin %s",
409     err_tp->toString ().c_str (), qualified_name);
410     return YCPNull ();
411   }
412
413   // go call it now!
414   y2debug ("Ruby is calling builtin %s", qualified_name);
415   YCPValue ret_yv = bi_call->evaluate (false /* no const subexpr elim */);
416   delete bi_call;
417
418   return ret_yv;
419 }
420
421
422
423 //y2_logger_helper
424
425 //y2_logger (level, comp, file, line, function, "%s", message);
426
427 static VALUE
428 rb_y2_logger( int argc, VALUE *argv, VALUE self )
429 {
430   Check_Type(argv[0], T_FIXNUM);
431   Check_Type(argv[1], T_STRING);
432   Check_Type(argv[2], T_STRING);
433   Check_Type(argv[3], T_FIXNUM);
434   Check_Type(argv[4], T_STRING);
435
436   int i;
437   for ( i = 5; i < argc; i++)
438   {
439     Check_Type(argv[i], T_STRING);
440   }
441   y2_logger((loglevel_t)NUM2INT(argv[0]),RSTRING(argv[1])->ptr,RSTRING(argv[2])->ptr,NUM2INT(argv[3]),"",RSTRING(argv[5])->ptr);
442   return Qnil;
443 }
444
445 extern "C"
446 {
447   void
448   Init_yastx()
449   {
450     YCPPathSearch::initialize ();
451
452     for ( list<string>::const_iterator it = YCPPathSearch::searchListBegin (YCPPathSearch::Module); it != YCPPathSearch::searchListEnd (YCPPathSearch::Module) ; ++it )
453     {
454       y2internal("%s\n", (*it).c_str() );
455     }
456
457     rb_mYaST = rb_define_module("YaST");
458     rb_mUi = rb_define_module_under(rb_mYaST, "Ui");
459     rb_define_singleton_method( rb_mUi, "init", RB_METHOD(rb_init_ui), -1);
460     
461     rb_define_method( rb_mYaST, "y2_logger", RB_METHOD(rb_y2_logger), -1);
462     
463     rb_cBroker = rb_define_class_under( rb_mYaST, "Module", rb_cObject);
464     //rb_define_singleton_method( rb_cBroker, "new", RB_METHOD(module_new), 1);
465     rb_define_alloc_func(rb_cBroker, yast_module_allocate);
466     rb_define_method(rb_cBroker, "initialize", RB_METHOD(yast_module_initialize), 1);
467     rb_define_method( rb_cBroker, "method_missing", RB_METHOD(yast_module_proxy_method), -1);
468     rb_define_method( rb_cBroker, "name", RB_METHOD(yast_module_name), -1);
469
470     ryast_path_init(rb_mYaST);
471     ryast_term_init(rb_mYaST);
472   }
473 }