Mercurial > hg > Papers > 2021 > anatofuz-master
changeset 84:88ae1e4d83c6
update
author | anatofuz <anatofuz@cr.ie.u-ryukyu.ac.jp> |
---|---|
date | Fri, 05 Feb 2021 11:01:56 +0900 |
parents | 7f5bb7c5b433 |
children | c6c4b103c705 |
files | paper/chapter/03-gears.tex paper/chapter/04-interface.tex paper/master_paper.pdf paper/src/Interface.pm paper/src/InterfaceParse.pm paper/src/IsInterface.pm paper/src/createHeaderName2Info.pl |
diffstat | 7 files changed, 569 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/paper/chapter/03-gears.tex Fri Feb 05 10:24:40 2021 +0900 +++ b/paper/chapter/03-gears.tex Fri Feb 05 11:01:56 2021 +0900 @@ -455,15 +455,3 @@ - - \section{Interfaceの取り扱い方法の検討} - - GearsOSのInterfaceはモジュール化の仕組みと\texttt{goto}文での引数の一時保管場所としての機能を持っている。 -InterfaceのImplementのヘッダーファイルを実装したことで、 GearsOS上でInterfaceを実装する際に新たな方法での実装を検討した。 -ImplementのCodeGearは今まではInterfaceで定義したCodeGearと1対1対応していた。 -ImplementのCodeGearからgotoする先は、 入力として与えられたCodeGearか、 Implement内で独自に定義したCodeGearにgotoするケースとなっていた。 -後者の独自に定義したCodeGearにgotoするケースも、 実装のCbCファイルの中に記述されているCodeGearに遷移していた。 - -GearsOSを用いてxv6 OSを再実装した際に、 実装側のCodeGearを細かく別けて記述した。 -細分化によって1つのCbCファイルあたりのCodeGearの記述量が増えてしまうという問題が発生した。 -見通しをよくする為に、 Interfaceで定義したCodeGearと直接対応するCodeGearの実装と、 それらからgotoするCodeGearで実装ファイルを分離することを試みた。
--- a/paper/chapter/04-interface.tex Fri Feb 05 10:24:40 2021 +0900 +++ b/paper/chapter/04-interface.tex Fri Feb 05 11:01:56 2021 +0900 @@ -1,4 +1,10 @@ \chapter{GearsOSのInterfaceの改良} +GearsOSのモジュール化の仕組みであるInterfaceは、 GearsOSの中心的な機能である。 +Interfaceの取り扱いには様々なメタ計算が含まれ、 このメタ計算はPerlスクリプトによって生成される。 + +InterfaceをGearsOSで使ったプログラミングをするにつれて、様々な不足している機能や、改善すべき点が見つかった。 +またPerlスクリプトがInterfaceを適切に取り扱う為のAPIも必要となることが分かった。 +本章では本研究で行ったGearsOSのInterfaceの改良について述べる。 \section{GearsOSのInterfaceの構文の改良} GearsOSのInterfaceでは、 従来はDataGearとCodeGearを分離して記述していた。 @@ -47,7 +53,8 @@ 構文を変更するには、 GearsOSのビルドシステム上でInterfaceを利用している箇所を修正する必要がある。 Interfaceはgenerate\_stub.plで読み込まれ、 CodeGearと入出力のDataGearの数え上げが行われる。 この処理はInterfaceのパースに相当するものである。 -当然ではあるが、パース対象のInterfaceの構文は、変更前の構文にしか対応していない。 +パース対象のInterfaceの構文は、変更前の構文にしか対応していなかった。 +後方互換性を維持したまま、新しい構文に対応させるために、generate\_stub.plが利用するInterfaceの解析ルーチンを両方の構文に対応させた。 \section{Implementの型定義ファイルの導入} @@ -88,7 +95,12 @@ 特にプライベートメソッドがない場合は、 実装側で所持したい変数定義を記述する。 SynchronizedQueueの例では\texttt{top}などが実装側で所持している変数である。 \lstinputlisting[label=src:syncqueue, caption=SynchronizedQueueの定義ファイル]{src/SynchronizedQueue.h} -従来context.hに直接記述していたすべてのDataGearの定義は、 スクリプトで機械的にInterfaceおよびImplementの型定義ファイルに変換している。 +従来context.hに直接記述していたすべてのDataGearの定義は、 スクリプトで機械的にInterfaceおよびImplementの型定義ファイルに変換を行った。 + +context.hからInterfaceおよびImplementの型定義をファイルに分割することができた。 +しかしGearsOSのContextはすべてのDataGearの型定義を持つ必要がある。 +この為、context.hには分割した型定義ファイルをもとに、CbCのメタレベルに変換された型情報を書き込む必要がある。 +この処理はgenerate\_context.pl内でビルド時に行うようにした。 \section{Implementの型をいれたことによる間違ったGearsプログラミング} Implementの型を導入したが、 GearsOSのプログラミングをするにつれていくつかの間違ったパターンがあることがわかった。 @@ -111,18 +123,26 @@ 従来のGearsOSのトランスコンパイラでは、 generate\_stub.plがInterfaceファイルを開き、情報を解析していた。 この情報解析はgetDataGear関数で行われていた。 しかしこの関数は、CbCファイルのCodeGear、DataGearの解析で使用するルーチンと同じものである。 -従って、 Interface特有のパースが出来ていなかった。 +この為Interface特有のパースが出来ていなかった。 -例えば開いたヘッダファイルがInterfaceのファイルでも、そうでないCのヘッダファイルでも同様の解析をしてしまう。 +また、開いたヘッダファイルがInterfaceのファイルでも、そうでないCのヘッダファイルでも同様の解析をしてしまう。 Interfaceの定義ファイルの構文はすでに統一されたものを使用している。 -この構文で実装されていないInterfaceファイルを読み込んだ場合は、 エラーとして処理したい。 +Interfaceの定義の構文で実装されていないInterfaceファイルを読み込んだ場合は、 エラーとして処理したい。 また、Interfaceが満たすべきCodeGearの種類やInputDataGearの数の管理も行いたい。 さらにInterfaceではなく、Implementの定義ファイルも同様にパースし、情報を解析したい。 -これらを実現するには、最初からInterfaceに特化したパーサーが必要となる。 -本研究ではGears::Interfaceモジュールとして実装した。 +これらを実現するには、今までgenerate\_stub.plで使っていた情報解析ルーチンをもとに、最初からInterfaceに特化したパーサーが必要となる。 +本研究ではGears::InterfaceモジュールとしてInterfaceのパーサーを実装した。 \subsection{Gears::Interfaceの構成} +Gears::InterfaceはPerlのモジュールであるが、 実際はパーサー用のAPIを提供しているサブルーチンのまとまりである。 +その為オブジェクトを作らずに直接メソッドを呼び出して利用する。 + +パーサーはInterfaceであるかどうかを、構文の正規表現にマッチするかどうかで確認をする。(ソースコード\ref{src:IsInterface}) +\lstinputlisting[label=src:IsInterface, caption=Interfaceであるかどうかの確認]{src/IsInterface.pm} + +\subsection{Interfaceパーサーの呼び出し} +\lstinputlisting[label=src:createHeaderName2Info, caption=ヘッダファイルの名前とInterfaceのパース結果の対応リストの作製]{src/createHeaderName2Info.pl} \section{Interfaceの実装のCbCファイルへの構文の導入}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/src/Interface.pm Fri Feb 05 11:01:56 2021 +0900 @@ -0,0 +1,347 @@ +package Gears::Interface; +use strict; +use warnings; +use Carp qw/croak cluck confess/; +use File::Basename qw/dirname/; +use File::Spec; + +use Gears::Util; +#use DDP {deparse => 1}; + +sub parse { + # create this data structure + my ($class, $file) = @_; + my $ir = {}; + $ir->{file_name} = $file // confess "require file"; + + return undef unless (Gears::Util->file_checking($file)); + + open my $fh, '<', $file; + my $line = <$fh>; + my $static_data_gear_write_mode = 0; + + my $dir_name = dirname(File::Spec->rel2abs($file)); + + while ($line =~ /#include\s+"([\w\/\.]+)"/) { + my $header_file = $1; + if ($header_file =~ m|\./context\.h|) { + $line = <$fh>; + next; + } + push(@{$ir->{cbc_context_include_headers}}, "$dir_name/$header_file"); + $line = <$fh>; + } + + # skip space + + while ($line =~ /^\s*$/) { + $line = <$fh>; + } + + my $typed_variable = {}; + + if ($line =~ /typedef struct (\w+)\s?<(.*)>([\s\w{]+)/) { + my $vname = $1; + my $v_typed_variable = $2; + my $annotation = $3; + + unless ($vname) { + cluck "[WARN] invalied struct name from $file"; + return undef; + } + $ir->{name} = $vname; + + if ($v_typed_variable) { + $typed_variable = parse_header_typed_variable($v_typed_variable); + $ir->{typed_variable_order} = $typed_variable->{__order}; + } + + if ($annotation =~ m|\s*impl\s*([\w+]+)\s*{|) { + $ir->{isa} = $1; + } + } + + return undef unless ($ir->{name}); + + my @data_gears; + my %inner_code_gears; + + while ($line = <$fh>) { + chomp $line; + if ($line =~ m|\s*/\*|) { + while ( $line !~ m|\*/|) { + $line = <$fh>; + next; + } + next; + } + next if ($line =~ /^\s+$/); + next if ($line =~ m[^\s*//]); + next if ($line =~ m[^\}\s*$ir->{name};]); + + if ($line =~ m|__code (\w+)|) { + + push(@data_gears,"enum Code $1;"); #this case insert __code name (__code hoge -> enum Code hoge;) + + #In the case of writing field variables one line at a time, cancel the following + next if $static_data_gear_write_mode; + + + my $args = $'; + #$args eq (Impl* vm, pde_t* pgdir, char* init, uint sz, __code next(...)); + while ($args =~ /\s*(struct|union|const|enum)?\s*([<>\w\[\]_]+)(\*)?\s*(\w+)?,?/g) { + my $const_type = $1; + my $type = $2; + my $pointer = $3 // ""; + my $vname = $4; + + if ($type eq '__code') { + $inner_code_gears{$vname} = 1; #collect inner code gears (ex. next, whenEmpty) + next; + } + next unless $vname; # __code hoge(int ret, __code next(ret, ...); this is second "ret" case + $type =~ s/^(?:Impl|Type|Isa)\s*(\*)?/union Data/; + + my $val = "$type$pointer $vname;"; + + parse_generics($val); + + my $generics_info; + if ($typed_variable) { + ($val, $generics_info) = check_use_typed_variable($ir->{name},$typed_variable, $val); #define typed variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{typed_variable}}) { + push(@{$ir->{typed_variable}},$generics_info); + push(@{$ir->{typed_variable_types}->{$generics_info->{type}}}, $generics_info->{vname}); + } + } + } + + $generics_info = {}; + ($val, $generics_info) = check_use_instance_generics_type($val); #define instance generics variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{generics}}) { + push(@{$ir->{generics}},$generics_info); + } + } + + push(@data_gears, $const_type ? "$const_type $val" : $val); + } + next; + } + + #this is a case of writing field variables one line at a time + $line =~ s/^\s+//; + parse_generics($line); + + my $generics_info; + if ($typed_variable && keys(%$typed_variable)) { + ($line, $generics_info) = check_use_typed_variable($ir->{name},$typed_variable, $line); #define typed variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{typed_variable}}) { + push(@{$ir->{typed_variable}},$generics_info); + push(@{$ir->{typed_variable_types}->{$generics_info->{type}}}, $generics_info->{vname}); + } + } + } + $generics_info = {}; + ($line, $generics_info) = check_use_instance_generics_type($line); #define typed variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{generics}}) { + push(@{$ir->{generics}},$generics_info); + } + } + push(@data_gears,$line); + $static_data_gear_write_mode = 1; + } + + $ir->{inner_code_gears} = \%inner_code_gears; + push(@{$ir->{content}}, Gears::Util->uniq(@data_gears)); + return $ir; +} + +# separate_code_and_data_gear_after_parse +sub detailed_parse { + my ($class, $file) = @_; + my $ir = Gears::Interface->parse($file); + + return undef unless ($ir); + + $ir->{hasOutputArgs} = {}; + + my @data_gears; + my @code_gears; + + for my $dg (@{$ir->{content}}) { + if ($dg =~ /enum Code (\w+);/) { + push(@code_gears, $1); + } else { + push(@data_gears, $dg); + } + } + + open my $fh , '<', $file; + my $i = 0; + my @have_output_data; + my @inner_code_gears; + + my @output_code_gears; + + while (($i < scalar @code_gears) && (my $line = <$fh>)) { + my $codeGearName = $code_gears[$i]; + + if (exists $ir->{inner_code_gears}->{$codeGearName}) { + $i++; + next; + } + + if ($line =~ m|__code $codeGearName\(([()\.\*\s\w,_]+)\)|) { + my $arg = $1; + + # check individual argument + # eg. (Impl* self, Int a, char* b, struct hoge* h, __code next(out* o, out* o2, ...)) + # indivisual argument is self, a, b, h, next ( $argc == 5) + my @comma_split_arg = split /,/, $arg; + my $inParen = undef; + my $argc = 0; + for my $tmpArg (@comma_split_arg) { + #ignore inner code gear (eg next) + if ($tmpArg =~ /\(/ ) { + $inParen = 1; + } + + # want to __code next(...) <- right paren + if ($tmpArg =~ /\)/) { + $inParen = undef; + } + + next if ($inParen); + $argc++; + } + #ignore self + # ex upper case, before $argc == 5, after $argc == 4 + if (@comma_split_arg) { + $argc-- if ($comma_split_arg[0] =~ /Impl/); + } + my $element = { name => $codeGearName, args => $arg, argc => $argc }; + push(@output_code_gears, $element); + + #code gear name to hash + $ir->{codeName}->{$codeGearName} = $element; + + # args eq "Impl* stack, __code next(Type* data, Type* data1, ...)", + if ($arg =~ /__code \w+\((.+),\s*\.\.\.\s*\)/) { + my $outputArgs = $1; + while ($outputArgs =~ /(struct|union|const|enum)?\s*([\w*]+)\s(\w+),?/g) { + my $structType = $1; + my $ttype = $2; + my $tname = $3; + $ir->{hasOutputArgs}->{$codeGearName}->{$tname} = $ttype; + } + } + $i++; + } + } + + + $ir->{codes} = \@output_code_gears; + $ir->{data} = \@data_gears; + return $ir; +} + + +sub isThisFileInterface { + my ($class, $filename) = @_; + + open my $fh, '<', $filename; + my $line = <$fh>; #read top line ex Typedef struct Stack<Type, Impl> { + + return 0 unless ($line =~ /typedef struct \w+\s?<.*>([\s\w{]+)/); + + my $annotation = $1; + return 0 if ($annotation =~ /impl/); + + return 1; +} + +sub parse_header_typed_variable { + #parsed typedef struct Hoge <S, T> { + my $str = shift; + my @pair_generics = split /,/, $str; + my %typed_variable; + for my $pear (@pair_generics) { + $pear =~ s/\s*(\w+)/$1/; + my ($type, $border) = split /:/, $pear; + confess "failed parsed type at $pear\n" unless $type; + unless ($border) { + $border = "_any"; #_any is union Data* + } + $typed_variable{$type} = $border; + push(@{$typed_variable{__order}},$type); + } + return \%typed_variable; +} + +sub parse_generics { + my $line = shift; + return unless ($line =~ /([\w\*]+)\s*<([\w\*]+)>/); + print "[INFO] use generics $1 $2 at $line\n"; +} + +sub collect_interfaces_from_all_headers { + my ($class, $find_path) = @_; + my $header_files = Gears::Util->find_headers_from_path($find_path); + my @result = sort grep { Gears::Interface->isThisFileInterface($_) } @$header_files; + return \@result; +} + +sub check_use_typed_variable { + my ($caller, $typed_variable, $line) = @_; + if ($line =~ /(\w+)(\*?)\s+(\w+);?$/) { + my $type = $1; + my $pointer = $2 // ''; + my $vname = $3; + + my $typed_info = { type => $type, vname => $vname}; + + if (exists $typed_variable->{$type}) { + if ($typed_variable->{$type} eq "_any") { + #$line = "TYPED_GENERICS_${caller}_$type $pointer $vname;"; #no extend case + } else { + #extend case + #$line = "EXTEND_TYPED_GENERICS_${type}_$typed_variable->{$type}$pointer $vname;"; # <T:Say> -> T_say + $typed_info->{extends} = $typed_variable->{$type}; + } + + return ($line, $typed_info); + } + } + return ($line, {}); +} + +sub check_use_instance_generics_type { + #define typed variable + my $line = shift; + my $not_use = {}; + my $use = {}; + unless ($line =~ /(\w+)<(\w+)>(\*)?\s+(\w+);/) { + return ($line, $not_use); + } + my $type = $1; + my $generics = $2; + my $pointer = $3; + my $vname = $4; + + unless ($pointer) { + $pointer = ""; + } + + my $upGenerics = ucfirst($generics); + $use->{type} = $type; + $use->{generics} = $generics; + $use->{vname} = $vname; + #return ("INTERFACE_GENERICS_${type}_$upGenerics$pointer $vname;", $use); + return ("$type$pointer $vname;", $use); +} + +1;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/src/InterfaceParse.pm Fri Feb 05 11:01:56 2021 +0900 @@ -0,0 +1,161 @@ +package Gears::Interface; +use strict; +use warnings; +use Carp qw/croak cluck confess/; +use File::Basename qw/dirname/; +use File::Spec; + +use Gears::Util; + +sub parse { + # create this data structure + my ($class, $file) = @_; + my $ir = {}; + $ir->{file_name} = $file // confess "require file"; + + return undef unless (Gears::Util->file_checking($file)); + + open my $fh, '<', $file; + my $line = <$fh>; + my $static_data_gear_write_mode = 0; + + my $dir_name = dirname(File::Spec->rel2abs($file)); + + while ($line =~ /#include\s+"([\w\/\.]+)"/) { + my $header_file = $1; + if ($header_file =~ m|\./context\.h|) { + $line = <$fh>; + next; + } + push(@{$ir->{cbc_context_include_headers}}, "$dir_name/$header_file"); + $line = <$fh>; + } + + # skip space + + while ($line =~ /^\s*$/) { + $line = <$fh>; + } + + my $typed_variable = {}; + + if ($line =~ /typedef struct (\w+)\s?<(.*)>([\s\w{]+)/) { + my $vname = $1; + my $v_typed_variable = $2; + my $annotation = $3; + + unless ($vname) { + cluck "[WARN] invalied struct name from $file"; + return undef; + } + $ir->{name} = $vname; + + if ($v_typed_variable) { + $typed_variable = parse_header_typed_variable($v_typed_variable); + $ir->{typed_variable_order} = $typed_variable->{__order}; + } + + if ($annotation =~ m|\s*impl\s*([\w+]+)\s*{|) { + $ir->{isa} = $1; + } + } + + return undef unless ($ir->{name}); + + my @data_gears; + my %inner_code_gears; + + while ($line = <$fh>) { + chomp $line; + if ($line =~ m|\s*/\*|) { + while ( $line !~ m|\*/|) { + $line = <$fh>; + next; + } + next; + } + next if ($line =~ /^\s+$/); + next if ($line =~ m[^\s*//]); + next if ($line =~ m[^\}\s*$ir->{name};]); + + if ($line =~ m|__code (\w+)|) { + + push(@data_gears,"enum Code $1;"); #this case insert __code name (__code hoge -> enum Code hoge;) + + #In the case of writing field variables one line at a time, cancel the following + next if $static_data_gear_write_mode; + + + my $args = $'; + #$args eq (Impl* vm, pde_t* pgdir, char* init, uint sz, __code next(...)); + while ($args =~ /\s*(struct|union|const|enum)?\s*([<>\w\[\]_]+)(\*)?\s*(\w+)?,?/g) { + my $const_type = $1; + my $type = $2; + my $pointer = $3 // ""; + my $vname = $4; + + if ($type eq '__code') { + $inner_code_gears{$vname} = 1; #collect inner code gears (ex. next, whenEmpty) + next; + } + next unless $vname; # __code hoge(int ret, __code next(ret, ...); this is second "ret" case + $type =~ s/^(?:Impl|Type|Isa)\s*(\*)?/union Data/; + + my $val = "$type$pointer $vname;"; + + parse_generics($val); + + my $generics_info; + if ($typed_variable) { + ($val, $generics_info) = check_use_typed_variable($ir->{name},$typed_variable, $val); #define typed variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{typed_variable}}) { + push(@{$ir->{typed_variable}},$generics_info); + push(@{$ir->{typed_variable_types}->{$generics_info->{type}}}, $generics_info->{vname}); + } + } + } + + $generics_info = {}; + ($val, $generics_info) = check_use_instance_generics_type($val); #define instance generics variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{generics}}) { + push(@{$ir->{generics}},$generics_info); + } + } + + push(@data_gears, $const_type ? "$const_type $val" : $val); + } + next; + } + + #this is a case of writing field variables one line at a time + $line =~ s/^\s+//; + parse_generics($line); + + my $generics_info; + if ($typed_variable && keys(%$typed_variable)) { + ($line, $generics_info) = check_use_typed_variable($ir->{name},$typed_variable, $line); #define typed variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{typed_variable}}) { + push(@{$ir->{typed_variable}},$generics_info); + push(@{$ir->{typed_variable_types}->{$generics_info->{type}}}, $generics_info->{vname}); + } + } + } + $generics_info = {}; + ($line, $generics_info) = check_use_instance_generics_type($line); #define typed variable + if (defined $generics_info->{type}){ + unless (grep {$_->{vname} eq $generics_info->{vname} } @{$ir->{generics}}) { + push(@{$ir->{generics}},$generics_info); + } + } + push(@data_gears,$line); + $static_data_gear_write_mode = 1; + } + + $ir->{inner_code_gears} = \%inner_code_gears; + push(@{$ir->{content}}, Gears::Util->uniq(@data_gears)); + return $ir; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/src/IsInterface.pm Fri Feb 05 11:01:56 2021 +0900 @@ -0,0 +1,13 @@ +sub isThisFileInterface { + my ($class, $filename) = @_; + + open my $fh, '<', $filename; + my $line = <$fh>; #read top line ex Typedef struct Stack<Type, Impl> { + + return 0 unless ($line =~ /typedef struct \w+\s?<.*>([\s\w{]+)/); + + my $annotation = $1; + return 0 if ($annotation =~ /impl/); + + return 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/paper/src/createHeaderName2Info.pl Fri Feb 05 11:01:56 2021 +0900 @@ -0,0 +1,21 @@ +sub createHeaderNameToInfo { + my ($fn, $search_root) = @_; + my $dirname = dirname $fn; + my $files = Gears::Util->find_headers_from_path($search_root); + my $interface_name2headerpath = {}; + + #This process assumes that there are no header files of the same name + for my $file (@{$files}) { + if ($file =~ m|/(\w+)\.\w+$|) { + my $file_name = $1; + my $isInterface = Gears::Interface->isThisFileInterface($file); + if (defined $interface_name2headerpath->{$file_name}) { + next if ($file !~ m|$dirname|); + } + $interface_name2headerpath->{$file_name} = { path => $file, isInterface => $isInterface }; + } + } + + return $interface_name2headerpath; +} +