# Copyright (C) 2001 Tobias Peters # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Library General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. class Idl_Method attr_reader :interface attr_reader :name attr_reader :return_type attr_reader :parameters attr_reader :idl_module def raises_exception? @raises_exception end def to_s @name end def initialize(idl_module, interface, remaining_lines, attribut_method = false) @idl_module = idl_module @interface = interface @attribut_method = attribut_method @raises_exception = false @parameters = [] # extract return type and method name from declaration match = /\s*([^\s].*[^\s])\s+([^\s]+)\s*\((.*)/.match(remaining_lines[0]) @name = match[2] @return_type = match[1] # cut first of remaining lines to the part behind the opening "(": remaining_lines[0] = match[3] if (not attribut_method) puts("Method #{@return_type} #{interface.name()}::#{@name}") if ($verbose) end # read parameters current_line = nil begin current_line = remaining_lines[0] # extract parameter type and name if there is another parameter match = /in\s+([^\s].*[^\s])\s+([^\s]+)[,\)]/.match(remaining_lines[0]) if (match) @parameters << Idl_Parameter.new(@idl_module, match[1], match[2], attribut_method) end remaining_lines.delete_at(0) end while ((current_line =~ /\)/) == nil) # stop when ")" closes params list # read exception if (remaining_lines[0] =~ /\s+raises\s*\(/) @raises_exception = true remaining_lines.delete_at(0) puts("... raises exception") if ((attribut_method == false) && $verbose) end end #################### def create_ruby_code() # The start of the function definition that implements this method's # functionality. More parameters are added to this C function's # parameters list as we iterate over the @parameters of this method methods_code = ("static VALUE #{rbdom_method_name(@interface, self)} (VALUE self ") # local variable definitions. These will be placed directly after the # opening brace of the C function block. More local variables (C equivalents # of the ruby parameters) will be added to this list as we iterate over the # @parameters of this method c_definitions = (" VALUE rb_return_value = Qnil; \n" + " GdomeException e = GDOME_NOEXCEPTION_ERR; \n" + " #{gdome_class_name(@interface)} * c_self = \n" + " #{rb2c_name(@interface)}(self); \n") # the call to the gdome function that implements this method. We will also # add more parameters here. c_call = gdome_method_name(@interface, self) + "(c_self, " # when we translate ruby strings to GdomeDOMStrings for a gdome function # call, then we will need to unref these strings again after the gdome # function call took place. We will place the unref statements in this # string as we iterate over the @parameters and find DOMString parameters c_unref_domstrings = "" # Now iterate over the parameters and do all these additions @parameters.each {|p| methods_code << (",\n VALUE rb_#{p.name}") # conversion to C depending on the type of the parameter case p.ptype when "boolean" c_definitions << (" GdomeBoolean c_#{p.name} = \n" + " !(rb_#{p.name} == Qfalse \n" + " || rb_#{p.name} == Qnil); \n") when "unsigned long" c_definitions << (" unsigned long c_#{p.name} = NUM2ULONG(rb_#{p.name}); \n") when "unsigned short", "unsigned int" c_definitions << (" #{p.ptype} c_#{p.name} = NUM2UINT(rb_#{p.name}); \n") when "DOMString" c_definitions << (" #{gdome_class_name(p.ptype)} * c_#{p.name} = \n" + " (rb_#{p.name} == Qnil) ? NULL : \n" + " gdome_str_mkref_dup(STR2CSTR(rb_#{p.name})); \n") # A DOMString needs to be unref'd after the call c_unref_domstrings << (" if (c_#{p.name}) gdome_str_unref(c_#{p.name}); \n") when "string" c_definitions << (" char * c_#{p.name} = \n" + " (rb_#{p.name} == Qnil) ? NULL : \n" + " STR2CSTR(rb_#{p.name}); \n") else c_definitions << (" #{gdome_class_name(p.ptype)} * c_#{p.name} = \n" + " #{rb2c_name(p.ptype)}(rb_#{p.name}); \n") end c_call << (" c_#{p.name},") } # close parameters lists methods_code << ")\n" c_call << " &e)" # place the locale variable definitions at the start of the function methods_code << ("{ \n" + c_definitions ) # place the call to the gdome function and convert the return value to ruby case @return_type when "void" methods_code << (" #{c_call}; \n") when "boolean" methods_code << (" rb_return_value = #{c_call} ? Qtrue : Qfalse; \n") when "unsigned short", "unsigned long" methods_code << (" rb_return_value = INT2NUM(#{c_call}); \n") when "DOMString" methods_code << (" { \n" + " GdomeDOMString * ds = #{c_call}; \n" + " rb_return_value = ds ? rb_str_new2(ds->str) : Qnil; \n" + " if (ds) gdome_str_unref(ds); \n" + " } \n") when "string" methods_code << (" { \n" + " char * cs = #{c_call}; \n" + " rb_return_value = cs ? rb_str_new2(cs) : Qnil; \n" + " if (cs) free(cs); \n" + " } \n") else methods_code << (" rb_return_value = #{c2rb_name(@return_type)}(#{c_call}); \n") end # unref all GdomeDOMString parameters methods_code << c_unref_domstrings # Check if an exception was raised if (raises_exception? || "ALWAYS CHECK FOR EXCEPTIONS") methods_code << (" if (e != GDOME_NOEXCEPTION_ERR) { \n" + " rb_raise(#{rbdom_class_name('DOMException')}, \n" + " \"%d\", (int)e); \n" + " } \n") end methods_code << (" return rb_return_value; \n" + "} \n") # some ruby-ization of method names ruby_method_name = @name if (@attribut_method && (@parameters.size() == 1) && (ruby_method_name =~ /^set_/)) ruby_method_name = ruby_method_name.sub(/^set_/, "") + "=" elsif (@return_type == "boolean") ruby_method_name += "?" end # bind this method to its class init_func_code = (" rb_define_method(#{rbdom_class_name(@interface)}, \n" + " \"#{ruby_method_name}\", \n" + " &#{rbdom_method_name(@interface, self)}, \n" + " #{@parameters.size()}); \n") @idl_module.add_ruby_code("methods", methods_code) @idl_module.add_ruby_code("init_func", init_func_code) end ################### def create_cpp_code(gdome_cpp_hh, gdome_cpp_cc) # This does basically the same as the create_ruby_code method, only for # C++. see implementation comments there for explanations gdome_cpp_hh << " #{cpp_type(@return_type)}\n" declaration = " #{self}(" definition = "#{cpp_type(@return_type)} #{@interface}::#{self}(" locals = (" GdomeException e = 0; \n" + " #{gdome_class_name(@interface)} * c_self = get_gdome_obj(); \n") c_call = gdome_method_name(@interface, self) + "(c_self, " c_unref_domstrings = "" separator = "" @parameters.each {|p| # determine wether we pass the parameter by value or by reference if (Gdome_ABBREVIATIONS[p.ptype] == nil) p_ref = "" else p_ref = "&" end p_ref = ("const " + p_ref) if (p.ptype == 'DOMString') # commando back. Using references would save us some copying, but it # would mostly imply const references, otherwise it would be very # difficult to pass NULL objects here and there (Compiler would say # initialization of non-const reference type `class Gdome::DocumentType &' # from rvalue of type `Gdome::DocumentType' and the like). p_ref = "" # all parameters are passed by value. declaration << ("#{separator}#{cpp_type(p.ptype)} #{p_ref} #{p.name}") definition << ("#{separator}#{cpp_type(p.ptype)} #{p_ref} #{p.name}") separator = ", " c_call << ("c_#{p.name}#{separator}") if (p.ptype == "boolean") locals << (" GdomeBoolean c_#{p.name} = #{p.name}; \n") elsif ((p.ptype == "unsigned long") || (p.ptype == "unsigned short")) locals << (" #{cpp_type(p.ptype)} c_#{p.name} = #{p.name}; \n") elsif (p.ptype == "DOMString") locals << (" GdomeDOMString * c_#{p.name} = \n" + " gdome_str_mkref_dup(#{p.name}.gc_str()); \n") c_unref_domstrings << (" if (c_#{p.name}) gdome_str_unref(c_#{p.name}); \n") else locals << (" #{gdome_class_name(p.ptype)} * c_#{p.name} = \n" + " #{p.name}.get_gdome_obj(); \n") end } if (raises_exception?) declaration << ")" definition << ")\n throw (DOMException)\n" else declaration << ");" definition << ")\n" end c_call << "&e)" definition << ("{ \n" + locals ) if (@return_type == "void") definition << (" #{c_call}; \n") elsif ((@return_type == "boolean") || (@return_type == "unsigned short") || (@return_type == "unsigned long")) definition << (" #{cpp_type(@return_type)} return_value = #{c_call}; \n") elsif (@return_type == "DOMString") definition << (" GdomeDOMString * ds = #{c_call}; \n" + " DOMString return_value = ds ? ds->str : \n" + " static_cast(0); \n" + " if (ds) gdome_str_unref(ds); \n") else definition << (" #{cpp_type(@return_type)} return_value( \n" + " #{c_call} \n" + " ); \n") end definition << c_unref_domstrings if (raises_exception?) definition << (" if (e != 0) throw DOMException(e,\"#{@interface}::#{self}\");\n") end definition << " return return_value; \n" if (@return_type != "void") definition << "} \n" while (declaration.length > 80) breakpoint = declaration.rindex(",", 79) + 1 gdome_cpp_hh << (declaration[0,breakpoint] + "\n") declaration = " " + declaration[breakpoint, 1000] end if (raises_exception?) declaration << "\n throw (DOMException);" end gdome_cpp_hh << declaration + "\n\n" gdome_cpp_cc << definition end end