Ignore:
Timestamp:
Jul 3, 2020, 7:19:17 PM (4 years ago)
Author:
coas-nagasima
Message:

ASP3, TINET, mbed を更新

File:
1 edited

Legend:

Unmodified
Added
Removed
  • EcnlProtoTool/trunk/asp3_dcre/tecsgen/tecslib/plugin/MrubyBridgePlugin.rb

    r321 r429  
    33#  mruby => TECS bridge
    44
    5 #   Copyright (C) 2008-2015 by TOPPERS Project
     5#   Copyright (C) 2008-2017 by TOPPERS Project
    66#
    77#   上記著作権者は,以下の(1)~(4)の条件を満たす場合に限り,本ソフトウェ
     
    3737#
    3838
    39 # Todo:
    40 # 生成するもの
    41 #  gen_cdl_file
    42 #    ブリッジ初期化セルタイプ (シグニチャごと)
    43 #       @@init_celltypes に記録
    44 #    ブリッジセルタイプ (シグニチャごと)
    45 #       @@celltypes に記録
    46 #    構造体セルタイプ
    47 #       @@struct_list に記録
    48 #  new_cell
    49 #    TECS 初期化セル(プロトタイプ宣言)
    50 #       @@VM_init_cells に記録
    51 #       @@VM_struct_list に記録
    52 #       @@VM_ptr_list に記録
    53 #  gen_post_code
    54 #    構造体初期化セル
    55 #    ポインタ初期化セル
    56 #    TECS 初期化セル
    57 #
    58 #   ep_func の作成
    59 #     signature, ポインタ、構造体 … 初期化受け口=mruby VM への登録
    60 #     ポインタ
    61 #   preamble の作成
    62 #     ポインタ、構造体 … アクセス用コードの生成
    63 #     signature … ブリッジ関数のプロトタイプ宣言
    64 #   postamble の作成
    65 #     signature … ブリッジ関数の定義
    66 
    67 class MrubyBridgePlugin < SignaturePlugin
    68 
    69   # プラグイン引数名 => Proc
    70   MrubyBridgePluginArgProc = {
    71       "ignoreUnsigned" => Proc.new { |obj,rhs| obj.set_ignoreUnsigned rhs },
    72       "include" => Proc.new { |obj,rhs| obj.set_include rhs },
    73       "exclude" => Proc.new { |obj,rhs| obj.set_exclude rhs },
    74   }
    75 
    76   @@celltypes = { }             # {celltype_name => [ BridgePlugin のインスタンスの配列 }
    77   @@init_celltypes = { }        # {celltype_name => [ BridgePlugin のインスタンスの配列 }
    78   @@struct_list = { }           # {struct_name=>StructType}
    79   @@ptr_list = { }              # {ptr_celltype_name=> @@TYPE_MAP の対応するもの}
    80   @@VM_list = { }               # VM_name => true
    81   @@VM_celltypes = {  }             # VM_name => { @celltype_name => セルの配列 }
    82   @@VM_struct_list = { }           # {name=>StructType}
    83   @@VM_ptr_list = { }              # { VM_name => {name=> @@TYPE_MAP の対応するもの} }
    84   @@TYPE_MAP = {           # type_str   class             GET_SET
    85     :char_t            => [:char_t,    "Char",     :Char,  :INT   ],
    86     :uchar_t           => [:uchar_t,   "UChar",    :Char,  :INT   ],
    87     :schar_t           => [:schar_t,   "SChar",    :Char,  :INT   ],
    88 
    89     :bool_t            => [:bool_t,    "Bool",     :Bool,  :BOOL  ],
    90     :int8_t            => [:int8_t,    "Int8",     :Int,   :INT   ],
    91     :int16_t           => [:int16_t,   "Int16",    :Int,   :INT   ],
    92     :int32_t           => [:int32_t,   "Int32",    :Int,   :INT   ],
    93     :int64_t           => [:int64_t,   "Int64",    :Int,   :INT   ],
    94     :uint8_t           => [:uint8_t,   "UInt8",    :Int,   :INT   ],
    95     :uint16_t          => [:uint16_t,  "UInt16",   :Int,   :INT   ],
    96     :uint32_t          => [:uint32_t,  "UInt32",   :Int,   :INT   ],
    97     :uint64_t          => [:uint64_t,  "UInt64",   :Int,   :INT   ],
    98 
    99     :int               => [:int,       "Int",      :Int,  :INT   ],
    100     :char              => [:char,      "Char",     :Char, :INT   ],    # char は char_t として扱う
    101     :short             => [:short,     "Short",    :Int,  :INT   ],
    102     :long              => [:long,      "Long",     :Int,  :INT   ],
    103 
    104     :"unsigned char"   => [:uchar_t,   "UChar",         :Char, :INT   ],
    105     :"unsigned int"    => [:"unsigned int",   "UInt",   :Int,  :INT   ],
    106     :"unsigned short"  => [:"unsigned short", "UShort", :Int,  :INT   ],
    107     :"unsigned long"   => [:"unsigned long",  "ULong",  :Int,  :INT   ],
    108     :"signed char"     => [:schar_t,   "SChar",    :Char,  :INT   ],
    109     :"signed int"      => [:int,       "Int",      :Int,   :INT   ],
    110     :"signed short"    => [:short,     "Short",    :Int,   :INT   ],
    111     :"signed long"     => [:long,      "Long",     :Int,   :INT   ],
    112 
    113     :float32_t         => [:float32_t, "Float32",  :Float, :FLOAT ],
    114     :double64_t        => [:double64_t,"Double64", :Float, :FLOAT ],
    115 
    116     :float             => [:float,     "Float32",  :Float, :FLOAT ],
    117     :double            => [:double,    "Double64", :Float, :FLOAT ]
    118   }
    119 
    120   # included  or excluded functions
    121 
    122   ### ロードされた時点で実行される ###
    123 
    124   # -I に $(TECSPATH)/mruby を追加
    125   # TECSGEN::Makefile.add_obj "$(MRUBY_MAIN_OBJ)"
    126   TECSGEN::Makefile.add_ldflag "-lmruby -L$(MRUBYPATH)/lib -lm"
    127   TECSGEN::Makefile.add_search_path "$(MRUBYPATH)/include"
    128   TECSGEN::Makefile.add_var "MRUBYPATH",      "..",  "CHANGE this to suitable path"
    129   # TECSGEN::Makefile.add_var "MRUBY_MAIN_OBJ", "$(_TECS_OBJ_DIR)tecs_mruby.o", "CHANGE this if your have your main"
    130 
    131 
    132   #=== プラグインインスタンスの初期化
    133   # 戻り値、引数の型が使用可能なものかチェックする
    134   #
    135   def initialize( signature, option )
    136     super
    137 
    138     if ! $no_banner
    139       STDERR << "MrubyBridgePlugin: version 1.2.0 (Suitable for mruby ver 1.2.0. Has backward compatibility with ver 1.1.0)\n"
    140     end
    141 
    142     @b_ignoreUnsigned = false
    143     @includes = []
    144     @excludes = []
    145     @struct_list = { }
    146     @ptr_list = { }
    147 
    148     @plugin_arg_check_proc_tab = MrubyBridgePluginArgProc
    149     parse_plugin_arg
    150 
    151     @celltype_name = :"t#{@signature.get_global_name}"
    152     @init_celltype_name = :"#{@celltype_name}_Initializer"
    153         # this variable is sometimes not used. rhs coded directry.
    154     @class_name = :"T#{@signature.get_global_name}"
    155 
    156     @func_head_array = []
    157     if @includes.length > 0 && @excludes.length > 0 then
    158       cdl_error( "MRB1011 both include && exclude are specified" )
    159     end
    160 
    161     if signature.get_function_head_array == nil then
    162       return   # 以前に文法エラー発生
    163     end
    164 
    165     signature.get_function_head_array.each{ |func_head|
    166       if @includes.length > 0 then
    167         if @includes.index func_head.get_name then
    168           dbgPrint "MrubyBridgePlugin: #{func_head.get_name} INCLUDED\n"
    169           @func_head_array << func_head
    170         else
    171           dbgPrint "MrubyBridgePlugin: #{func_head.get_name} NOT included\n"
    172         end
    173       elsif @excludes.length > 0 then
    174         if @excludes.index( func_head.get_name ) == nil then
    175           dbgPrint "MrubyBridgePlugin: #{func_head.get_name} NOT excluded\n"
    176           @func_head_array << func_head
    177         else
    178           dbgPrint "MrubyBridgePlugin: #{func_head.get_name} EXCLUDED\n"
    179         end
    180       else
    181         @func_head_array << func_head
    182       end
    183     }
    184 
    185     if @func_head_array.length == 0 then
    186       cdl_error( "MRB1012 '$1' no function remained by exclude", @signature.get_name )
    187     end
    188 
    189     check_name_and_return_type @func_head_array
    190     check_parameter_type @func_head_array
    191 
    192   end
    193 
    194   #=== check function name & return type
    195   def check_name_and_return_type func_head_array
    196     b_init = false; b_init_cell = false
    197     func_head_array.each{ |func_head|
    198       if( func_head.get_name == :initialize )then
    199         cdl_warning( "MRW2001 initialize: internally defined. change to initialize_cell in ruby" )
    200         b_init = true
    201       elsif( func_head.get_name == :initialize_cell )then
    202         b_init_cell = true
    203       end
    204       rtype = func_head.get_return_type.get_original_type
    205       case rtype
    206       when BoolType, IntType, FloatType, VoidType
    207       else
    208         cdl_error( "MRB1001 cannot return type $1", rtype.get_type_str )
    209       end
    210     }
    211     if( b_init && b_init_cell )then
    212       cdl_warning( "MRB1002 initialize: internally defined. change to initialize_cell in ruby" )
     39#== MrubyBridgePlugin クラス
     40class MrubyBridgePlugin < MultiPlugin
     41  def self.get_plugin superClass
     42    # case when (つまりは ===) では、期待したように一致しない模様
     43    if superClass == SignaturePlugin then
     44      dbgPrint "MrubyBridgePlugin: SignaturePlugin"
     45      require_tecsgen_lib 'tecslib/plugin/MrubyBridgeSignaturePlugin.rb'
     46      return MrubyBridgeSignaturePlugin
     47    elsif superClass == CelltypePlugin
     48      dbgPrint "MrubyBridgePlugin: CelltypePlugin"
     49      require_tecsgen_lib 'tecslib/plugin/MrubyBridgeCelltypePlugin.rb'
     50      return MrubyBridgeCelltypePlugin
     51    elsif superClass == CompositePlugin
     52      dbgPrint "MrubyBridgePlugin: CompositePlugin"
     53      require_tecsgen_lib 'tecslib/plugin/MrubyBridgeCompositePlugin.rb'
     54      return MrubyBridgeCompositePlugin
     55    elsif superClass == CellPlugin
     56      dbgPrint "MrubyBridgePlugin: CellPlugin"
     57      require_tecsgen_lib 'tecslib/plugin/MrubyBridgeCellPlugin.rb'
     58      return MrubyBridgeCellPlugin
     59    #elsif superClass == ThroughPlugin
     60    #  return ThroughPlugin
     61    #elsif superClass == DomainPlugin
     62    #  return DomainPlugin
     63    else
     64      dbgPrint "MrubyBridgePlugin: unsupported"
     65      return nil
    21366    end
    21467  end
    215 
    216   #=== check paramter type
    217   def check_parameter_type func_head_array
    218     # check type of parameters
    219     func_head_array.each{ |fh|
    220       fh.get_paramlist.get_items.each{ |param_decl|
    221         case param_decl.get_direction
    222         when :SEND, :RECEIVE
    223           cdl_error( "MRB1003 $1: $2 parameter cannot be used in mruby Bridge",  param_decl.get_name, param_decl.get_direction.to_s.downcase )
    224         end
    225         type = param_decl.get_type
    226         type_org = type.get_original_type
    227         type_str = type.get_type_str + type.get_type_str_post
    228 
    229         b_ng = false
    230         case type_org
    231         when IntType
    232           case  type_org.get_bit_size
    233           when 8, 16, 32, 64
    234           when -1, -2, -3, -4, -11
    235           else
    236             b_ng = true
    237           end
    238         when BoolType
    239         when FloatType
    240         when PtrType
    241           ttype_org = type_org.get_type       # ポインタの指している先の型
    242           ttype = ttype_org.get_original_type # 上記の typedef されている場合、元の型
    243           register_ptr_type ttype_org
    244 
    245           if( type_org.get_string.to_s == "-1" ) then
    246             case param_decl.get_direction
    247             when :OUT, :INOUT
    248               cdl_error( "MRB9999 string specifier without length cannot be used for out & inout parameter")
    249             end
    250           end
    251 
    252           case ttype
    253           when IntType
    254             bit_size = ttype.get_bit_size
    255             # if  bit_size < 0 && bit_size != -1 then
    256             #   b_ng = true
    257             # end
    258           when FloatType
    259           when BoolType
    260           when StructType
    261             if( type_org.get_size || type_org.get_string || type_org.get_count ) then
    262               cdl_error( "MRB1004 $1: size_is, count_is, string cannot be specified for struct pointer", param_decl.get_name )
    263             end
    264             check_struct_member ttype_org
    265           else
    266             b_ng = true
    267           end
    268         when StructType
    269           check_struct_member type_org
    270         else  # ArrayType, FuncType, EnumType, VoidType
    271           b_ng = true
    272         end
    273         if b_ng then
    274           cdl_error( "MRB1005 $1: type $2 cannot be used in mruby Bridge", param_decl.get_name, type_str )
    275         end
    276       }
    277     }
    278   end
    279 
    280   #=== 構造体のメンバーの型のチェック
    281   def check_struct_member struct_type
    282     #p "tag name:#{struct_type.get_name}"
    283     # sttype = Namespace.find_tag( struct_type.get_name )
    284     sttype = struct_type.get_original_type
    285     if sttype.get_name == nil then
    286       cdl_error( "MRB10007 tagless-struct cannot be handled")
    287     end
    288     sttype.get_members_decl.get_items.each { |d|
    289       t = d.get_type.get_original_type
    290       case t
    291       when IntType, FloatType, BoolType
    292       else
    293         cdl_error( "MRB1006 $1: type $2 not allowed for struct member", d.get_name, d.get_type.get_type_str + d.get_type.get_type_str_post )
    294       end
    295     }
    296     st_name = :"t{}"
    297     if @struct_list[ sttype.get_name ] == nil then
    298       # print_msg "  MrubyBridgePlugin: [struct]   #{struct_type.get_type_str} => class TECS::Struct#{sttype.get_name}\n"
    299       print "  MrubyBridgePlugin: [struct]   #{struct_type.get_type_str} => class TECS::Struct#{sttype.get_name}\n"
    300       @struct_list[ sttype.get_name ] = sttype
    301     end
    302   end
    303 
    304   def register_ptr_type ttype
    305     t_org = ttype.get_original_type
    306     tment = get_type_map_ent t_org
    307     if tment == nil then
    308       return
    309       cdl_error( "MRB1008 unknown pointer type '$1'", ttype.get_type_str )
    310     end
    311     ptr_celltype_name = :"t#{tment[1]}Pointer"
    312     if @@ptr_list[ ptr_celltype_name ] == nil then
    313       # print_msg "  MrubyBridgePlugin: [pointer]  #{ttype.get_type_str}* => class TECS::#{tment[1]}Pointer\n"
    314       print "  MrubyBridgePlugin: [pointer]  #{ttype.get_type_str}* => class TECS::#{tment[1]}Pointer\n"
    315       @@ptr_list[ ptr_celltype_name ] = tment
    316     end
    317     if @ptr_list[ ptr_celltype_name ] == nil then
    318       @ptr_list[ ptr_celltype_name ] = tment
    319     end
    320   end
    321 
    322   def get_type_map_ent ttype
    323     # structure type is registerd in check_struct_member
    324     if ttype.kind_of? StructType
    325       return
    326     end
    327     tstr = ttype.get_type_str.sub( /const /, "" )    # const は無視
    328     tstr = tstr.sub( /volatile /, "" )               # volatile も無視
    329     if @b_ignoreUnsigned then
    330       tstr = tstr.sub( /unsigned /, "" )             # volatile も無視
    331       tstr = tstr.sub( /uint/, "int" )               # volatile も無視
    332       tstr = tstr.sub( /[cs]char/, "char" )          # volatile も無視
    333     end
    334     return @@TYPE_MAP[ tstr.to_sym ]
    335   end
    336 
    337   #===  CDL ファイルの生成
    338   #      typedef, signature, celltype, cell コードを生成
    339   #file::        FILE       生成するファイル
    340   def gen_cdl_file(file)
    341 
    342     # ブリッジセルタイプの生成
    343     if @@celltypes[ @celltype_name ] == nil then
    344       @@celltypes[ @celltype_name ] = [ self ]
    345       @@init_celltypes[ @init_celltype_name ] = true
    346       print_msg <<EOT
    347   MrubyBridgePlugin: [signature] #{@signature.get_namespace_path} => [celltype] nMruby::#{@celltype_name} => [class] TECS::#{@class_name}
    348 EOT
    349 
    350       file.print <<EOT
    351 import( <mruby.cdl> );
    352 
    353 /****  Ruby => TECS Bridge Celltype (MBP500) ****/
    354 namespace nMruby{
    355     // bridge celltype
    356     [idx_is_id,active]   // not actually active, to avoid warning W1002, W1007
    357     celltype #{@celltype_name} {
    358         call #{@signature.get_namespace_path.to_s} cTECS;
    359         attr {
    360             [omit]
    361             char_t *VMname = "VM";
    362         };
    363     };
    364     // bridge initializer celltype
    365     celltype #{@init_celltype_name} {
    366         entry sInitializeTECSBridge eInitialize;
    367     };
    368 };
    369 EOT
    370 
    371       # 構造体セルタイプの生成
    372       @struct_list.each{ |name, sttype|
    373         if @@struct_list[ name ] == nil then
    374           file.print <<EOT
    375 namespace nMruby{
    376     [singleton]
    377     celltype #{name} {
    378         entry sInitializeTECSBridge eInitialize;
    379     };
    380 };
    381 EOT
    382           @@struct_list[ name ] = sttype
    383         end
    384       }
    385 
    386     else
    387       cdl_warning( "MRBW001 MrubyBridgePlugin: signature '$1' duplicate. ignored current one", @signature.get_namespace_path )
    388       @@celltypes[ @celltype_name ] << self
    389     end
    390   end
    391 
    392   #=== gen_cdl_file で定義したセルタイプに 新しいセルが定義された
    393   # cell のセルタイプの名前は @celltype_name
    394   def new_cell cell
    395     if cell.get_celltype.get_name != @celltype_name then
    396       return
    397     end
    398 
    399     join = cell.get_join_list.get_item :VMname
    400     if join then
    401       vm_name = CDLString.remove_dquote(join.get_rhs.to_s).to_sym
    402     else
    403       vm_name = :"VM"
    404     end
    405 
    406     if @@VM_list[ vm_name ] == nil then
    407       @@VM_list[ vm_name ] = true
    408 
    409       initializer_celltype_cdl = "#{$gen}/#{cell.get_name}Initializer.cdl"
    410       file = CFile.open( initializer_celltype_cdl, "w" )
    411 
    412       # TECS 初期化セル(プロトタイプ宣言)
    413       print_msg "  MrubyBridgePlugin: join your VM's cInitialize to #{vm_name}_TECSInitializer.eInitialize\n"
    414 
    415       file.print <<EOT
    416 
    417   // prototype of TECSInitializer (MBP510)
    418   cell nMruby::tTECSInitializer #{vm_name}_TECSInitializer;
    419 EOT
    420       file.close
    421 
    422       Generator.parse( initializer_celltype_cdl, self )
    423     end
    424 
    425     if @@VM_celltypes[ vm_name ] then
    426       vma = @@VM_celltypes[ vm_name ]
    427 
    428       if vma[ @celltype_name ] then
    429         vma[ @celltype_name ] << cell
    430       else
    431         vma[ @celltype_name ] = [cell]
    432         @@VM_celltypes[ vm_name ] = vma
    433       end
    434     else
    435       vma = { }
    436       vma[ @celltype_name ] = [cell]
    437       @@VM_celltypes[ vm_name ] = vma
    438     end
    439 
    440     @struct_list.each{ |stname, sttype|
    441       if @@VM_struct_list[ vm_name ] then
    442         @@VM_struct_list[ vm_name ][ sttype.get_name ] = sttype
    443       else
    444         @@VM_struct_list[ vm_name ] = { sttype.get_name => sttype }
    445       end
    446     }
    447     @ptr_list.each{ |ptr_celltype_name, tment|
    448       if @@VM_ptr_list[ vm_name ] then
    449         @@VM_ptr_list[ vm_name ][ ptr_celltype_name ] = tment
    450       else
    451         @@VM_ptr_list[ vm_name ] = { ptr_celltype_name => tment }
    452       end
    453     }
    454 
    455   end
    456 
    457   #=== プラグインが CDL の POST コードを生成
    458   # tmp_plugin_post_code.cdl への出力
    459   def self.gen_post_code file
    460 
    461 #     file.print <<EOT
    462 # namespace nMruby {
    463 # EOT
    464 #
    465 #     @@ptr_list.each{ |name,tment|
    466 #       file.print <<EOT
    467 #
    468 #     // MBP600
    469 #     [singleton]
    470 #     celltype #{name} {
    471 #         entry  sInitializeTECSBridge eInitialize;
    472 #     };
    473 # EOT
    474 #     }
    475 #
    476 #     file.print <<EOT
    477 # };
    478 # EOT
    479 
    480 # gen_post_code で生成した celltype は gen_ep_func が呼び出されない #847
    481 #    @@struct_list.each{ |name,sttype|
    482 #      file.print <<EOT
    483 #
    484 #    [singleton]
    485 #    celltype #{name} {
    486 #        entry  sInitializeTECSBridge eInitialize;
    487 #    };
    488 #EOT
    489 #    }
    490 
    491     file.print "  // MBP601\n"
    492     @@VM_celltypes.each{ |vm_name, instance_list|
    493       instance_list.each { |celltype_name, array|
    494         cell = array[0]
    495         if cell.get_celltype then
    496           ct_name = cell.get_celltype.get_name
    497         file.print <<EOT
    498   cell nMruby::#{ct_name}_Initializer #{vm_name}_#{ct_name}_Initializer{ };
    499 EOT
    500         end
    501       }
    502     }
    503 
    504     file.print "  // MBP602\n"
    505     @@ptr_list.each{ |name,tment|
    506       file.print <<EOT
    507   cell nMruby::#{name} C#{name} { };
    508 EOT
    509     }
    510 
    511     file.print "  // MBP603\n"
    512     @@struct_list.each{ |name,sttype|
    513       file.print <<EOT
    514   cell nMruby::#{name} C#{name} { };
    515 EOT
    516     }
    517 
    518     if @@VM_celltypes == nil
    519       raise "are0"
    520     end
    521     @@VM_celltypes.each{ |vm_name, instance_list|
    522       file.print "  /* === VM #{vm_name} === (MBP610) */\n"
    523       init_cell_name = "#{vm_name}_TECSInitializer"
    524 
    525       file.print "  cell nMruby::tTECSInitializer #{init_cell_name} {\n"
    526 
    527       instance_list.each { |celltype_name, array|
    528         array.each{ |cell|
    529           ct_name = cell.get_celltype.get_name
    530           file.print "    cInitialize[] = #{vm_name}_#{ct_name}_Initializer.eInitialize;\n"
    531         }
    532       }
    533       if @@VM_ptr_list[vm_name] then
    534         @@VM_ptr_list[vm_name].each{ |name, tment|
    535           file.print "    cInitialize[] = C#{name}.eInitialize;\n"
    536         }
    537       end
    538       if @@VM_struct_list[vm_name] then
    539         @@VM_struct_list[vm_name].each{ |name, sttype|
    540           file.print "    cInitialize[] = C#{name}.eInitialize;\n"
    541         }
    542       end
    543       file.print "  };"
    544     }
    545    
    546   end
    547 
    548   ####### 以下コード生成段階 ######
    549 
    550   #===  受け口関数の本体コードを生成(頭部と末尾は別途出力)
    551   #ct_name:: Symbol    (プラグインで生成された) セルタイプ名 .Symbol として送られてくる
    552   def gen_ep_func_body( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    553     if @@celltypes[ ct_name ] then
    554       gen_ep_func_body_bridge( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    555     elsif @@init_celltypes[ ct_name ] then
    556       gen_ep_func_body_bridge_init( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    557     elsif @@ptr_list[ ct_name ] then
    558       gen_ep_func_body_ptr( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    559     elsif @@struct_list[ ct_name ] then
    560       gen_ep_func_body_struct( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    561     else
    562       raise "Unknown #{ct_name}"
    563     end
    564   end
    565 
    566   def gen_ep_func_body_bridge( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    567     raise "unexpected "
    568   end
    569 
    570   def gen_ep_func_body_bridge_init( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    571     file.print <<EOT
    572     CELLCB *p_cellcb = GET_CELLCB( idx );  /* no error check */     /* MBP700 */
    573     struct RClass       *rc;
    574 
    575     rc = mrb_define_class_under( mrb, TECS, \"#{@class_name}\", mrb->object_class );
    576     mrb_define_method( mrb, rc, "initialize", MrubyBridge_#{@celltype_name}_initialize, MRB_ARGS_REQ(1) );
    577     MRB_SET_INSTANCE_TT(rc, MRB_TT_DATA);
    578 EOT
    579 
    580     @func_head_array.each{ |f|
    581       if ! f.is_function? then
    582         next
    583       end
    584       if f.get_name != :initialize then
    585         func_name = f.get_name
    586       else
    587         func_name = :initialize_cell
    588       end
    589 
    590       ret_type = f.get_return_type
    591       n_param = 0
    592       f.get_paramlist.get_items.each{ |param|
    593         case param.get_direction
    594         when :IN, :INOUT, :OUT
    595           n_param += 1
    596         when :SEND, :RECEIVE
    597           raise "send, receive"
    598         end
    599       }
    600       if n_param > 0 then
    601         p_str = "MRB_ARGS_REQ( #{n_param} )"
    602       else
    603         p_str = "MRB_ARGS_NONE()"
    604       end
    605       file.print <<EOT
    606         mrb_define_method( mrb, rc, "#{func_name}", MrubyBridge_#{@celltype_name}_#{func_name}, #{p_str} );
    607 EOT
    608     }
    609   end
    610 
    611   def gen_ep_func_body_ptr( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    612    
    613     t = @@ptr_list[ct_name]
    614     type = t[1]
    615     file.print <<EOT
    616         struct RClass *a;                                /* MBP710 */
    617 
    618     a = mrb_define_class_under(mrb, TECS, "#{type}Pointer", mrb->object_class);
    619     MRB_SET_INSTANCE_TT(a, MRB_TT_DATA);
    620 
    621     mrb_define_method(mrb, a, "initialize",      #{type}Pointer_initialize,   MRB_ARGS_REQ(1));
    622     mrb_define_method(mrb, a, "[]",              #{type}Pointer_aget,         MRB_ARGS_REQ(1));
    623     mrb_define_method(mrb, a, "value",           #{type}Pointer_get_val,      MRB_ARGS_NONE());
    624     mrb_define_method(mrb, a, "[]=",             #{type}Pointer_aset,         MRB_ARGS_REQ(2));
    625     mrb_define_method(mrb, a, "value=",          #{type}Pointer_set_val,      MRB_ARGS_REQ(1));
    626     mrb_define_method(mrb, a, "size",            #{type}Pointer_size,         MRB_ARGS_NONE());
    627     mrb_define_method(mrb, a, "length",          #{type}Pointer_size,         MRB_ARGS_NONE());
    628 EOT
    629 
    630     if t[2] == :Char then
    631       file.print <<EOT
    632         mrb_define_method(mrb, a, "to_s",            CharPointer_to_s, MRB_ARGS_NONE());
    633         mrb_define_method(mrb, a, "from_s",          CharPointer_from_s, MRB_ARGS_REQ(1));
    634 EOT
    635     end
    636   end
    637 
    638   def gen_ep_func_body_struct( file, b_singleton, ct_name, global_ct_name, sig_name, ep_name, func_name, func_global_name, func_type, params )
    639     tag = ct_name
    640     structType = @@struct_list[ tag ]
    641     file.print  <<EOT
    642         struct RClass *a;                                /* MBP720 */
    643 
    644         a = mrb_define_class_under(mrb, TECS, "Struct#{tag}", mrb->object_class);
    645         MRB_SET_INSTANCE_TT(a, MRB_TT_DATA);
    646 
    647         mrb_define_method(mrb, a, "initialize", Struct_#{tag}_initialize, MRB_ARGS_NONE());
    648 EOT
    649 
    650       structType.get_members_decl.get_items.each{ |d|
    651         file.print "  STRUCT_INIT_MEMBER( #{tag}, #{d.get_name} )\n"
    652       }
    653   end
    654 
    655   #===  受け口関数の preamble (C言語)を生成する
    656   #     必要なら preamble 部に出力する
    657   #file::           FILE        出力先ファイル
    658   #b_singleton::    bool        true if singleton
    659   #ct_name::        Symbol
    660   #global_ct_name:: string
    661   def gen_preamble( file, b_singleton, ct_name, global_ct_name )
    662     if @@celltypes[ ct_name ] then
    663       gen_preamble_mruby( file, b_singleton, ct_name, global_ct_name )
    664       gen_preamble_instance( file, b_singleton, ct_name, global_ct_name )
    665       gen_preamble_instance_initialize( file, b_singleton, ct_name, global_ct_name )
    666       gen_preamble_bridge_func( file, b_singleton, ct_name, global_ct_name )
    667     elsif @@init_celltypes[ ct_name ] then
    668       gen_preamble_mruby( file, b_singleton, ct_name, global_ct_name )
    669       gen_preamble_instance_proto( file, b_singleton, ct_name, global_ct_name )
    670     elsif @@ptr_list[ ct_name ] then
    671       gen_preamble_ptr( file, b_singleton, ct_name, global_ct_name )
    672     elsif @@struct_list[ ct_name ] then
    673       gen_preamble_struct( file, b_singleton, ct_name, global_ct_name )
    674     else
    675       raise "Unknown #{ct_name}"
    676     end
    677   end
    678 
    679   def gen_preamble_mruby( file, b_singleton, ct_name, global_ct_name )
    680     file.print <<EOT
    681 /* MBP: MrubyBridgePlugin: MBP000 */
    682 #include "mruby.h"
    683 #include "mruby/class.h"
    684 #include "mruby/data.h"
    685 #include "mruby/string.h"
    686 #include "TECSPointer.h"
    687 #include "TECSStruct.h"
    688 
    689 #if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
    690 #define DLLEXPORT __declspec(dllexport)
    691 #else
    692 #define DLLEXPORT
    693 #endif
    694 
    695 #ifndef NULL
    696 #define NULL 0
    697 #endif
    698 EOT
    699   end
    700 
    701   def gen_preamble_instance( file, b_singleton, ct_name, global_ct_name )
    702     file.print <<EOT
    703 
    704 /* RData MBP001 */
    705 static void
    706 #{@celltype_name}_free( mrb_state *mrb, void *p )
    707 {
    708         if( p )
    709                 (void)mrb_free( mrb, p );
    710 }
    711 
    712 /* RData MBP002 */
    713 struct mrb_data_type data_type_#{@celltype_name} =
    714 {
    715         "#{@celltype_name}",
    716         #{@celltype_name}_free
    717 };
    718 
    719 /* RData MBP003 */
    720 struct tecs_#{@celltype_name} {
    721     CELLCB  *cbp;
    722 };
    723 
    724 /* name_to_cbp MBP010 */
    725 const struct name_to_cbp_#{@celltype_name} {
    726     char   *name;    /* Cell Name */
    727     CELLCB *cbp;
    728 } Name_to_cbp_#{@celltype_name}[] = {
    729 EOT
    730 
    731     # mikan namespace
    732     nsp = NamespacePath.new( :nMruby, true )
    733     nsp.append! ct_name
    734     ct = Namespace.find nsp
    735 
    736     ct.get_cell_list.each{ |cell|
    737       if cell.is_generate? then
    738         name_array = ct.get_name_array( cell )
    739         file.print "\t{ \"#{cell.get_name}\", #{name_array[8]} },\n"
    740       end
    741     }
    742 
    743     file.print <<EOT
    744 \t{ 0, 0 },
    745 };
    746 
    747 EOT
    748 
    749   end
    750 
    751   def gen_preamble_ptr( file, b_singleton, ct_name, global_ct_name )
    752     tment = @@ptr_list[ ct_name ]
    753     file.print <<EOT
    754 
    755 GET_SET_#{tment[3]}( #{tment[1]}, #{tment[0]} )
    756 POINTER_CLASS( #{tment[1]}, #{tment[0]} )
    757 EOT
    758   end
    759 
    760   def gen_preamble_struct( file, b_singleton, ct_name, global_ct_name )
    761     tag = ct_name
    762     structType = @@struct_list[ tag ]
    763     file.print <<EOT
    764 /* struct #{tag} */
    765 STRUCT_CLASS( #{tag} )
    766 EOT
    767 
    768     structType.get_members_decl.get_items.each{ |d|
    769       type = d.get_type.get_original_type
    770       case type
    771       when IntType, CIntType
    772         bit_size = type.get_bit_size
    773         case bit_size
    774         when -11, -1
    775           tType = "Char"
    776           ttype = "char"
    777         when -2
    778           tType = "Short"
    779           ttype = "short"
    780         when -3
    781           tType = "Int"
    782           ttype = "int"
    783         when -4
    784           tType = "Long"
    785           ttype = "long"
    786         when -5
    787           tType = "IntPtr"
    788           ttype = "intptr"
    789         when 8, 16, 32, 64
    790           tType = "Int#{bit_size}"
    791           ttype = "int#{bit_size}"
    792         else
    793             raise "cannot handle bit_size #{bit_size}"
    794         end
    795         file.print "MEMBER_GET_SET_INT( #{tag}, #{d.get_name}, #{tType}, #{ttype} )\n"
    796       when FloatType, CFloatType
    797         file.print "MEMBER_GET_SET_FLOAT( #{tag}, #{d.get_name} )\n"
    798       else
    799         raise "cannot handle type"
    800       end
    801     }
    802 
    803   end
    804 
    805   def gen_preamble_instance_proto( file, b_singleton, ct_name, global_ct_name )
    806     file.print <<EOT
    807 //  Prototype MBP400
    808 mrb_value  MrubyBridge_#{@celltype_name}_initialize( mrb_state *mrb, mrb_value self);
    809 EOT
    810 
    811     @func_head_array.each{ |f|
    812       if ! f.is_function? then
    813         next
    814       end
    815       if f.get_name != :initialize then
    816         func_name = f.get_name
    817       else
    818         func_name = :initialize_cell
    819       end
    820 
    821       ret_type  = f.get_return_type
    822       ret_type0 = f.get_return_type.get_original_type
    823       b_void    = ret_type0.is_void?
    824       plist     = f.get_paramlist.get_items
    825 
    826       file.print <<EOT
    827 mrb_value  MrubyBridge_#{@celltype_name}_#{func_name}( mrb_state *mrb, mrb_value self );
    828 EOT
    829     }
    830   end
    831 
    832   def gen_preamble_instance_initialize( file, b_singleton, ct_name, global_ct_name )
    833     file.print <<EOT
    834 
    835 /* MBP100 */
    836 mrb_value
    837 MrubyBridge_#{@celltype_name}_initialize( mrb_state *mrb, mrb_value self)
    838 {
    839         mrb_value       name;
    840         struct tecs_#{@celltype_name} *tecs_cb;
    841         const struct name_to_cbp_#{@celltype_name} *ntc;
    842 
    843         /* set DATA_TYPE earlier to avoid SEGV */
    844         DATA_TYPE( self ) = &data_type_#{@celltype_name};
    845 
    846         mrb_get_args(mrb, "o", &name );
    847         if( mrb_type( name ) != MRB_TT_STRING ){
    848                 mrb_raise(mrb, E_NAME_ERROR, "cell name not string");
    849         }
    850         for( ntc = &Name_to_cbp_#{@celltype_name}[0]; ntc->name != NULL; ntc++ ){
    851                 if( strcmp( ntc->name, RSTRING_PTR( name ) ) == 0 )
    852                         break;
    853         }
    854         if( ntc->name == 0 ){
    855                 mrb_raise(mrb, E_ARGUMENT_ERROR, "cell not found");
    856         }
    857         tecs_cb = (struct tecs_#{@celltype_name} *)mrb_malloc(mrb, sizeof(struct tecs_#{@celltype_name}) );
    858         tecs_cb->cbp = ntc->cbp;
    859         DATA_PTR( self ) = (void *)tecs_cb;
    860 
    861         return self;
    862 }
    863 EOT
    864   end
    865 
    866   def gen_preamble_bridge_func( file, b_singleton, ct_name, global_ct_name )
    867 
    868     @func_head_array.each{ |f|
    869       if ! f.is_function? then
    870         next
    871       end
    872       if f.get_name != :initialize then
    873         func_name = f.get_name
    874       else
    875         func_name = :initialize_cell
    876       end
    877 
    878       ret_type  = f.get_return_type
    879       ret_type0 = f.get_return_type.get_original_type
    880       b_void    = ret_type0.is_void?
    881       plist     = f.get_paramlist.get_items
    882 
    883       file.print <<EOT
    884 
    885 /* bridge function (MBP101) */
    886 mrb_value
    887 MrubyBridge_#{ct_name}_#{func_name}( mrb_state *mrb, mrb_value self )
    888 {
    889         /* cellcbp (MBP105) */
    890         CELLCB  *p_cellcb = ((struct tecs_#{@celltype_name} *)DATA_PTR(self))->cbp;
    891 EOT
    892      
    893       file.print "      /* variables for return & parameter (MBP110) */\n"
    894       if ! b_void then
    895         file.print "    ", ret_type.get_type_str, "\tret_val", ret_type.get_type_str_post, ";\n"
    896       end
    897       arg_str = ""
    898       n_param  = 0
    899       n_scalar = 0
    900       n_ptr    = 0
    901       n_struct = 0
    902       plist.each{ |param|
    903         case param.get_direction
    904         when :IN, :INOUT, :OUT
    905           type = param.get_type.get_original_type
    906           case type
    907           when IntType
    908             file.print "        mrb_int mrb_", param.get_name, ";\n"
    909             file.print "        #{param.get_type.get_type_str}  #{param.get_name}#{param.get_type.get_type_str_post};\n"
    910             arg_str += "i"
    911             n_param += 1
    912             n_scalar += 1
    913           when FloatType
    914             file.print "        mrb_float       mrb_", param.get_name, ";\n"
    915             file.print "        #{param.get_type.get_type_str}  #{param.get_name}#{param.get_type.get_type_str_post};\n"
    916             arg_str += "f"
    917             n_param += 1
    918             n_scalar += 1
    919           when BoolType
    920             file.print "        mrb_value       mrb_", param.get_name, ";\n"
    921             file.print "        #{param.get_type.get_type_str}  #{param.get_name}#{param.get_type.get_type_str_post};\n"
    922             arg_str += "o"
    923             n_param += 1
    924             n_scalar += 1
    925           when PtrType
    926             file.print "        mrb_value       mrb_", param.get_name, ";\n"
    927             file.print "        #{param.get_type.get_type_str}  #{param.get_name}#{param.get_type.get_type_str_post};\n"
    928             arg_str += "o"
    929             n_param += 1
    930             n_ptr += 1
    931           when StructType
    932             file.print "        mrb_value       mrb_", param.get_name, ";\n"
    933             file.print "        #{param.get_type.get_type_str}  *#{param.get_name}#{param.get_type.get_type_str_post};\n"
    934             arg_str += "o"
    935             n_param += 1
    936             n_struct += 1
    937           else
    938             raise "Unkown type"
    939           end
    940         end
    941       }
    942 
    943       if n_param > 0 then
    944         file.print "    /* retrieve arguments (MBP111) */\n"
    945         file.print "    mrb_get_args(mrb, \"#{arg_str}\""
    946         plist.each{ |param|
    947           case param.get_direction
    948           when :IN, :INOUT, :OUT
    949             type = param.get_type.get_original_type
    950             case type
    951             when IntType
    952               file.print ", &mrb_", param.get_name
    953             when FloatType
    954               file.print ", &mrb_", param.get_name
    955             when BoolType
    956               file.print ", &mrb_", param.get_name
    957             when PtrType
    958               file.print ", &mrb_", param.get_name
    959             when StructType
    960               file.print ", &mrb_", param.get_name
    961             else
    962               raise "Unkown type"
    963             end
    964           end
    965         }
    966         file.print " );\n"
    967 
    968         if n_scalar > 0 || n_struct > 0 then
    969           file.print "  /* convert mrb to C (MBP112) */\n"
    970         end
    971         plist.each{ |param|
    972           case param.get_direction
    973           when :IN, :INOUT, :OUT
    974             type = param.get_type.get_original_type
    975             case type
    976             when IntType
    977               ttype = type.get_original_type
    978               tment = get_type_map_ent ttype
    979               file.print "      VALCHECK_#{tment[1]}( mrb, mrb_#{param.get_name} );\n"
    980               file.print "      #{param.get_name} = (#{param.get_type.get_type_str})mrb_#{param.get_name};\n"
    981             when FloatType
    982               file.print "      #{param.get_name} = (#{param.get_type.get_type_str})mrb_#{param.get_name};\n"
    983             when BoolType
    984               file.print "      #{param.get_name} = mrb_test( mrb_#{param.get_name} );\n"
    985             when PtrType
    986               ttype = type.get_type.get_original_type
    987               case ttype
    988               when StructType
    989                 file.print "    CHECK_STRUCT( #{ttype.get_name}, mrb_#{param.get_name} );\n"
    990                 file.print "    #{param.get_name} = (struct #{ttype.get_name}*)DATA_PTR(mrb_#{param.get_name});\n"
    991               when IntType
    992               when FloatType
    993               when BoolType
    994               else
    995                 raise "cannot handle type"
    996               end
    997             when StructType
    998               file.print "      CHECK_STRUCT( #{type.get_name}, mrb_#{param.get_name} );\n"
    999               file.print "      #{param.get_name} = (struct #{type.get_name}*)DATA_PTR(mrb_#{param.get_name});\n"
    1000             else
    1001               raise( "canot treat class" )
    1002             end
    1003           end
    1004         }
    1005 
    1006         if n_ptr > 0 then
    1007           file.print "  /* convert mrb to C for pointer types (MBP113) */\n"
    1008         end
    1009         plist.each{ |param|
    1010           case param.get_direction
    1011           when :IN, :INOUT, :OUT
    1012             type = param.get_type.get_original_type
    1013             case type
    1014             when IntType
    1015             when FloatType
    1016             when BoolType
    1017             when PtrType
    1018               case type.get_type.get_original_type
    1019               when StructType
    1020               when IntType
    1021                 ptrMrb2C file, type, param
    1022               when FloatType
    1023                 ptrMrb2C file, type, param
    1024               when BoolType
    1025                 ptrMrb2C file, type, param
    1026               else
    1027                 raise "cannot handle type"
    1028               end
    1029             when StructType
    1030             else
    1031               raise( "canot treat class" )
    1032             end
    1033           end
    1034         }
    1035 
    1036       end
    1037 
    1038       file.print "      /* calling target (MBP120) */\n"
    1039       if ! b_void then
    1040         file.print "    ret_val = "
    1041       else
    1042         file.print "    "
    1043       end
    1044       delim = ""
    1045       file.print "cTECS_", f.get_name, "( "
    1046       plist.each{ |param|
    1047         if param.get_type.get_original_type.kind_of? StructType then
    1048           aster = "*"
    1049         else
    1050           aster = ""
    1051         end
    1052         file.print delim, aster, param.get_name
    1053         delim = ", "
    1054       }
    1055       file.print " );\n"
    1056      
    1057       file.print "      /* return (MBP130) */\n"
    1058       case ret_type0
    1059       when BoolType
    1060         file.print "    return ret_val ? mrb_true_value() : mrb_false_value();\n"
    1061       when IntType
    1062         file.print "    return mrb_fixnum_value( ret_val );\n"
    1063       when FloatType
    1064         file.print "    return mrb_float_value( mrb, ret_val );\n"
    1065       when VoidType
    1066         file.print "    return  mrb_nil_value();\n"
    1067       else
    1068         raise "unknown type"
    1069       end
    1070 
    1071       file.print "}\n"
    1072     }
    1073   end
    1074 
    1075   def ptrMrb2C file, type, param
    1076     ttype = type.get_type.get_original_type
    1077     tment = get_type_map_ent ttype
    1078     tstr = tment[1]
    1079 =begin
    1080     case ttype
    1081     when IntType
    1082       bit_size = ttype.get_bit_size
    1083       case bit_size
    1084       when -1, -11
    1085         tstr = "Char"
    1086       when 8, 16, 32, 64
    1087         tstr = "Int#{bit_size}"
    1088       when -2
    1089         tstr = "Short"
    1090       when -3
    1091         tstr = "Int"
    1092       when -4
    1093         tstr = "Long"
    1094       when -5
    1095         tstr = "IntPtr"
    1096       else
    1097         raise "not handle type"
    1098       end
    1099     when FloatType
    1100       if ttype.get_bit_size == 32 then
    1101         tstr = "Float32"
    1102       else
    1103         tstr = "Double64"
    1104       end
    1105     when BoolType
    1106       tstr = "Bool"
    1107     when StructType
    1108       raise "not handle type 2 #{ttype}"
    1109     else
    1110       raise "not handle type 2 #{ttype}"
    1111     end
    1112 =end
    1113     if( param.get_size ) then
    1114       sz_str = param.get_size.to_s
    1115     elsif param.get_string then      # mikan とりあえず size_is と string の同時指定 (二重ポインタ) はなし
    1116       sz_str = param.get_string.to_s
    1117     else
    1118       sz_str = "1"
    1119     end
    1120     # unsigned 型の場合には cast が必要
    1121     if ttype.get_original_type.get_type_str != param.get_type.get_type.get_type_str then
    1122       cast_str = "(#{param.get_type.get_type_str})"
    1123     else
    1124       cast_str = ""
    1125     end
    1126 
    1127     modify = ""
    1128     case param.get_direction
    1129     when :OUT, :INOUT
    1130       case tstr
    1131       when "Char", "SChar", "UChar"
    1132         modify = "Mod"
    1133       end
    1134     end
    1135     if param.is_nullable? then
    1136       nullable = "Nullable"
    1137     else
    1138       nullable = ""
    1139     end
    1140 
    1141     # file.print "      CHECK_POINTER( #{tstr}, mrb_#{param.get_name}, #{sz_str} );\n"
    1142     # file.print "      #{param.get_name} = #{cast_str}((struct #{tstr}PointerBody*)(DATA_PTR(mrb_#{param.get_name})))->buf;\n"
    1143     file.print "        #{param.get_name} = CheckAndGet#{tstr}Pointer#{modify}#{nullable}( mrb, mrb_#{param.get_name}, #{sz_str} );\n"
    1144   end
    1145 
    1146   def get_celltype_name
    1147     @celltype_name
    1148   end
    1149 
    1150   #=== プラグイン引数 ignoreUnsigned
    1151   def set_ignoreUnsigned rhs
    1152     if rhs == "true" || rhs == nil then
    1153       @b_ignoreUnsigned = true
    1154     end
    1155   end
    1156   #=== プラグイン引数 include
    1157   def set_include rhs
    1158     funcs = rhs.split ','
    1159     funcs.each{ |rhs_func|
    1160       found = false
    1161       rhs_func.gsub!( /\s/, "" )
    1162       @signature.get_function_head_array.each{ |a|
    1163         if rhs_func.to_sym == a.get_name then
    1164           found = true
    1165         end
    1166       }
    1167       if found == false then
    1168         cdl_error( "MRB1009 include function '$1' not found in signagture '$2'", rhs, @signature.get_name )
    1169       else
    1170         @includes << rhs_func.to_sym
    1171       end
    1172     }
    1173   end
    1174   #=== プラグイン引数 exclude
    1175   def set_exclude rhs
    1176     funcs = rhs.split ','
    1177     funcs.each{ |rhs_func|
    1178       found = false
    1179       rhs_func.gsub!( /\s/, "" )
    1180       @signature.get_function_head_array.each{ |a|
    1181         if rhs_func.to_sym == a.get_name then
    1182           found = true
    1183         end
    1184       }
    1185       if found == false then
    1186         cdl_error( "MRB1010 exclude function '$1' not found in signagture '$2", rhs, @signature.get_name )
    1187       else
    1188         @excludes << rhs_func.to_sym
    1189       end
    1190     }
    1191   end
    119268end
    1193 
Note: See TracChangeset for help on using the changeset viewer.