2020/08/25

2020/08/25

# 研究目的

  • OSの信頼性を保証する必要がある
  • 信頼性の保証にはモデル検査や定理証明を使用したい
    • 継続ベースの状態遷移系で再実装することで表現しやすくしたい
  • 既存のunixであるxv6をCbCで書き換えて、検証を行いやすくしたい
    • kernel/user両方をCbCで実装する

# 今週

  • ひたすらPerlを書いていた
  • generate_*.plにuse warningsを対応した
  • Gearefのインデントをきれいにした
  • Perlのテストを書き始めた
  • Outputがある場合のstubの自動生成ができた

# generate_*.plのwarnings対応

  • perlの場合use warningsすると未定義値を使ったケースなどでwarningが出るようになる
    • 万能ではないけどあるとバグの検知には便利
      • もっと色々解析する場合はCPANモジュールいれる必要あるので今回はそこまでしない
    • 今までのperlスクリプトには無かったので導入した

# generate_stub.pl

いろいろでる

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
\1 better written as $1 at generate_stub.pl line 523.
\1 better written as $1 at generate_stub.pl line 523.
\1 better written as $1 at generate_stub.pl line 694.
\1 better written as $1 at generate_stub.pl line 694.
\1 better written as $1 at generate_stub.pl line 698.
\1 better written as $1 at generate_stub.pl line 698.
Scalar value @dataGears[...] better written as $dataGears[...] at generate_stub.pl line 630.
Scalar value @dataGears[...] better written as $dataGears[...] at generate_stub.pl line 633.
Use of uninitialized value $1 in string ne at generate_stub.pl line 174, <$fd> line 4.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 5.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 5.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 6.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 2.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 2.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 3.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 3.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 4.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 4.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 5.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 5.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 6.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 6.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 7.
Use of uninitialized value $const_type in pattern match (m//) at generate_stub.pl line 197, <$fd> line 7.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 2.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 3.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 4.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 5.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 6.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 240, <$fd> line 7.
Reference found where even-sized list expected at generate_stub.pl line 423, <$in> line 57.
Use of uninitialized value $structType in pattern match (m//) at generate_stub.pl line 496, <$in> line 57.
Reference found where even-sized list expected at generate_stub.pl line 423, <$in> line 68.
Reference found where even-sized list expected at generate_stub.pl line 423, <$in> line 77.
Reference found where even-sized list expected at generate_stub.pl line 423, <$in> line 91.
  • わりと細々した修正がほとんど
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
             } elsif (! $inCode) {
-                s/new\s+(\w+)\(\)/\&ALLOCATE(${context_name}, \1)->\1/g;   # replacing new
+                s/new\s+(\w+)\(\)/\&ALLOCATE(${context_name}, $1)->$1/g;   # replacing new
                 print $fd $_;
                 next;
             } elsif (/^(.*)goto (\w+)\-\>(\w+)\((.*)\);/) {
@@ -626,10 +633,10 @@
                     print $fd "${prev}GET_META($dataGear)->wait = createSynchronizedQueue(${context_name});\n";
                 }
                 for my $i (0..$inputCount-1) {
-                    print $fd "${prev}${context_name}\->task->data[${context_name}\->task->idg+$i] = (union Data*)@dataGears[$i];\n";
+                    print $fd "${prev}${context_name}\->task->data[${context_name}\->task->idg+$i] = (union Data*)$dataGears[$i];\n";
                 }
                 for my $i (0..$outputCount-1) {
-                    print $fd "${prev}${context_name}\->task->data[${context_name}\->task->odg+$i] = (union Data*)@dataGears[$inputCount+$i];\n";
+                    print $fd "${prev}${context_name}\->task->data[${context_name}\->task->odg+$i] = (union Data*)$dataGears[$inputCount+$i];\n";
                 }
  • interfaceの実装をしていた場合に生成するStub関連で未定義値($interface)がよく使われていたらしい
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    # interface var
    for my $ivar (keys %{$var{$interface}}) {
        #  input data gear field
        if ($varName eq $ivar) {
            if ($typeName eq $var{$interface}->{$ivar}) {
                if ($output) {
                    $dataGearName{$codeGearName} .= "\t$typeName$ptrType* O_$varName = &Gearef($context_name, $interface)->$varName;\n";
                    $outputVar{$codeGearName} .= "\t$typeName$ptrType $varName  __attribute__((unused))  = *O_$varName;\n";
                    return 1;
                }
                $dataGearName{$codeGearName} .= "\t$typeName$ptrType $varName = Gearef($context_name, $interface)->$varName;\n";
                return 1;
            }
        }
    }

    # interface continuation
    for my $cName (keys %{$code{$interface}}) {
        if ($varName eq $cName) {
            # continuation field
            $dataGearName{$codeGearName} .= "\tenum Code $varName = Gearef($context_name, $interface)->$varName;\n";
            return 1;
        }
    }
  • まとめてサブルーチン化して丁寧に条件を確認する方法に変えた
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
sub generateImplStubArgs {
    my ($codeGearName, $varName, $typeName, $ptrType, $output, $interfaceName, $isImpl) = @_;
    return 0 unless $isImpl;
    for my $ivar (keys %{$var{$interfaceName}}) {
        #  input data gear field
        if ($varName eq $ivar) {
            if ($typeName eq $var{$interfaceName}->{$ivar}) {
                if ($output) {
                    $dataGearName{$codeGearName} .= "\t$typeName$ptrType* O_$varName = &Gearef($context_name, $interfaceName)->$varName;\n";
                    $outputVar{$codeGearName} .= "\t$typeName$ptrType $varName  __attribute__((unused))  = *O_$varName;\n";
                    return 1;
                }
                $dataGearName{$codeGearName} .= "\t$typeName$ptrType $varName = Gearef($context_name, $interfaceName)->$varName;\n";
                return 1;
            }
        }
    }

    # interface continuation
    for my $cName (keys %{$code{$interfaceName}}) {
        if ($varName eq $cName) {
            # continuation field
            $dataGearName{$codeGearName} .= "\tenum Code $varName = Gearef($context_name, $interfaceName)->$varName;\n";
            return 1;
        }
    }
}

# generate_context.pl

  • ファイルハンドルの使い回しが原因
    • せっかくなのでサブルーチン化した
  • before
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    $codeGear{"start_code"} = "$ddir/$name-context.c";
    $codeGear{"exit_code"} = "$ddir/$name-context.c";
    $mCodeGear{"start_code"} = "$ddir/$name-context.c";
    $mCodeGear{"exit_code"} = "$ddir/$name-context.c";
    open my $fd,">","$ddir/extern.h" or die("can't open $ddir/extern.h $!");
    for my $code ( sort keys %codeGear ) {
        print $fd "extern __code ${code}_stub(struct Context*);\n";
    }
    for my $impl ( sort keys %constructor ) {
        my ($interface, $constructorArgs) = @{$constructor{$impl}};
        print $fd "extern ${interface}* create${impl}($constructorArgs);\n";
    }
    print $fd "\n";

    open my $fd,">","$ddir/enumCode.h" or die("can't open $ddir/enumCode.h $!");
    print $fd "enum Code {\n";
    for my $code ( sort keys %codeGear ) {
        print $fd "    C_${code},\n";
    }
    print $fd "};\n";

    my $code_init = '';
    for my $code ( sort keys %mCodeGear ) {
        $code_init .=  "    ${context_name}->code[C_${code}]    = ${code}_stub;\n";
    }
  • After
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
sub generateContext {
    $codeGear{"start_code"} = "$ddir/$name-context.c";
    $codeGear{"exit_code"} = "$ddir/$name-context.c";
    $mCodeGear{"start_code"} = "$ddir/$name-context.c";
    $mCodeGear{"exit_code"} = "$ddir/$name-context.c";

    generateExtern();
    generateEnumCode();
    generateContextCsource();
    generateEnumData();
    generateTypedefData();
    generateDataGearInit();
}

# perlライブラリの整備とテスト

  • Gears::Utilに押し込んでいたInterfaceのパーサーをGeas::Interfaceに移動
    • 他細々したリファクタリング等
  • いくつかの処理のテストをperlTests下に描き始めている
    • Interfaceのパース
    • generate_stubの挙動
  • generate_stub.plのテストは変換先の.cを予め用意しておき、生成した中身が一致しているかどうかの簡易なテスト
    • 関数単位でのテストをする場合は気合いれてリファクタリングする必要がある…

# generate_stub.pl

  • .cbc(Gearsで書かれたソース)を.c純粋なCbCに変換するスクリプト
  • 大きく呼ばれるサブルーチンの数は2つ
    • getDataGear($fn);
      • .cbcまたは.hを読み込んで出てきたCodeGearとDataGearを数える
        • typedefの有無でヘッダファイルか.cbcかを判定している
      • 最初はコマンドライン引数として与えられた.cbcをパースする
        • でてきた.hや.cbcを再帰的にパースしにいく
      • ここで一度.cbcが読み込まれる
    • generateDataGear($fn);
      • .cbcを読みながら同時に.cを生成する
      • 現状.cbcを読み進めたら戻せないので(配列にいれるとかすればできる)、.cを書き込む前に必要なデータ回収はgetDataGearでする必要がある
  • この2つのサブルーチンを使う関係で、比較的グローバル変数が多い
    • ループで書かないといけないので、どうしてもそうなるところがある…

# includeしているファイルのパスを解決

  • Gearsで自分で書いたヘッダファイルをincludeしようとする場合、ビルドする場所の関係でパスの指定が難しい
    • #include "hoge.h"していた場合、hoge.hがプロジェクトルート下にいた場合フルパスに置き換える
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 } elsif (/^#include "(.*).h"$/) {
     my $headerName = $1;
     if ($headerName =~ m|/?context$|) {
       print $fd $_;
       next;
     }

     # This process assumes that there are no header files of the same name
     my $path = $headerNameToInfo->{$headerName}->{path};
     unless ($path) {
       print $fd $_;
       next;
     }

     print $fd '#include "' .$path . '"';
     print $fd "\n";
     next;

# 出力があるInterfaceから値を取り出したい

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
__code pop2Test_StackTestImpl(struct StackTestImpl* stackTest, struct Stack* stack, __code next(...)) {
  goto stack->pop2(pop2Test1_StackTestImpl);
}

__code pop2Test3_StackTestImpl(struct StackTestImpl* stackTest, struct Stack* stack, __code next(...)) {
  String* str = NEW(String);
  str->size = 200;
  String* str1 = NEW(String);
  str1->size = 300;
  goto pop2Test1_StackTestImpl(stackTest, (union Data*)str, (union Data*)str1);
}


__code pop2Test1_StackTestImpl(struct StackTestImpl* stackTest, union Data* data, union Data* data1, struct Stack* stack, __code next(...)) {
    String* str = (String*)data;
    String* str2 = (String*)data1;

    printf("%d\n", str->size);
    printf("%d\n", str2->size);
    goto next(...);
}

__code pop2Test1_StackTestImpl_stub(struct Context* context) {
  struct StackTestImpl* stackTest = GearImpl(context, StackTest, stackTest);
  Data* data = Gearef(context, Stack)->data;
  Data* data1 = Gearef(context, Stack)->data1;
  Stack* stack = Gearef(context, StackTest)->stack;
  enum Code next = Gearef(context, StackTest)->next;
  goto pop2Test1_StackTestImpl(context, stackTest, data, data1, stack, next);
}
  • 考え方としてはこの感じ

  • 各__codeごとに引数と型名の組を保存

  • goto stack->pop(nextCodeGear)が見つかれば、どのInterfaceが呼ばれているのかをみる

  • interfaceが特定できれば、メソッドの定義を確認

    • 出力があるメソッドの場合は、出力として呼び出し元(Stack)のInterfaceに書き込まれる変数と型の組を取得
    • フラグを立てておいて.cを生成するタイミングでC_を変更しし、対応したstubを生成

# ローカル変数の取得

  • とりあえず__codeの引数と途中で生成しているローカル変数の変数名と型名の組を各__Codeごとに保存しておく
    • 引数でもらってるケース, createしているケースがあるため
    • stubを切り替える際に切り替える対象のCodeGearの名前が必要なので、__codeごとに保存しておく必要がある
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
            } elsif (/^\_\_code (\w+)\((.*)\)(.*)/) {
                my $codeGearName = $1;
                if ($codeGearName =~ /_stub$/) {
                  $stub{$codeGearName}->{static} = 1;
                  $stub{$codeGearName}->{wrote} = 1;
                  $currentCodeGear = undef;
                  next;
                }
                my $args = $2;
                my $cbc_source_path = $searchCbCFromCodeGearNameWCurrentFrileName->($codeGearName, $filename);
                if ($cbc_source_path) {
                    &getCodeGear($cbc_source_path);
                }
                my $vname2type = Gears::Util->parseCodeGearDeclarationArg($args);
                for my $vname (keys %$vname2type) {
                  $codeGearInfo->{$codeGearName}->{arg}->{$vname} = $vname2type->{$vname};
                }
                $currentCodeGear = $codeGearName;
            } elsif ((/^\s*(union|struct|const|enum)?\s*(\w+)(\*)\s+(\w+)\s+=/) && $currentCodeGear) { # collect local variables
                my $structType = $1;
                my $interfaceName = $2;
                my $instance = $4;
                $codeGearInfo->{$currentCodeGear}->{localVar}->{$instance} = $interfaceName;

# gotoしているタイミングでのハンドリング

  • 引数->ローカル変数の順でgoto hoge->foo(nextCode)のhogeの型を確認する
    • この時点で#intefaceしているかどうかを確認
    • 忘れていそうだったらwarningだしながら自動include
  • 出力する変数名とInterfaceを保存しつつ、stubの名前を決める
  • 今の所はgoto hoge->foo(nextCode)と1引数の場合のみ想定
    • goto hoge->foo(bar,baz,nextCode)の場合はまだ対応していない
    • Gearsの方はこのケースはないので…. xv6ならあるかも
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
            } elsif (/^(.*)goto (\w+)\-\>(\w+)\((.*)\);/) {
                # handling goto statement
                # determine the interface you are using, and in the case of a goto CodeGear with output, create a special stub flag
                my $prev = $1;
                my $instance = $2;
                my $method = $3;
                my $tmpArgs = $4;
                my $typeName = $codeGearInfo->{$currentCodeGear}->{arg}->{$instance};
                unless ($typeName) {
                  #this case is not __code arguments.
                  for my $localVar (keys %{$codeGearInfo->{$currentCodeGear}->{localVar}}) {
                    if ($localVar eq $instance) {
                      $typeName = $codeGearInfo->{$currentCodeGear}->{localVar}->{$localVar};
                      last;
                    }
                  }
                  unless ($typeName){
                    die "[ERROR] not found $instance type $.: $_\n";
                  }
                }
                unless (exists $call_interfaces{$filename}->{$typeName}) {
                  warn "[AUTOINCLUDE] Forget #interface '$typeName'  declaration in $filename";
                  includeInterface(\%call_interfaces, $filename, $typeName, $headerNameToInfo);
                }

                my $nextOutPutArgs = findExistsOutputDataGear($typeName, $method);
                my $outputStubElem = { modifyEnumCode => $currentCodeGear, createStubName => $tmpArgs };

                if ($nextOutPutArgs) {
                  my $tmpArgHash = {};
                  map { $tmpArgHash->{$_} = $typeName } @$nextOutPutArgs;

                  $outputStubElem->{args} = $tmpArgHash;

                  #We're assuming that $tmpArgs only contains the name of the next CodeGear.
                  #Eventually we need to parse the contents of the argument. (eg. @parsedArgs)
                  my @parsedArgs = split /,/ , $tmpArgs; #
                  if (scalar(@parsedArgs) != 1) {
                    warn '[WARN] TBD';
                  }

                  $generateHaveOutputStub->{counter}->{$tmpArgs}++;
                  $outputStubElem->{counter} = $generateHaveOutputStub->{counter}->{$tmpArgs};
                  $generateHaveOutputStub->{list}->{$currentCodeGear} = $outputStubElem;
                }

# C_の生成関連

  • nextを引数の最後に置くとC_nextになってしまうバグ

    • 同様にstack->popなどを引数の最後においた場合もC_stack->popになってしまう
  • C_が生成される箇所で変数名をみてよしなにする

    • 別のinterfaceから値を取ってくるケースで、stubを切り替えるのもここで行う
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  $p =~ s/^(.*)\s(\w+)//;
  $pType = $1;
  $pName = $2;
  $arg =~ s/^(\s)*(\w+)/$2/;
  if ($pType =~ s/\_\_code$//) {
      if ($arg =~ /(\w+)\(.*\)/) {
          print $fd "${indent}Gearef(${context_name}, $ntype)->$pName = $1;\n";
      } else {
          my $hasGotoArgOrLocalVar = undef;
          my $outputStubElem = $generateHaveOutputStub->{list}->{$codeGearName};

          if ($outputStubElem && !$stub{$outputStubElem->{createStubName}."_stub"}->{static}) {
            my $pick_next = "$outputStubElem->{createStubName}_$outputStubElem->{counter}";
            print $fd "${indent}Gearef(${context_name}, $ntype)->$pName = C_$pick_next;\n";
            $i++;
            next;
          }

          # find __code of argument or local variable
          for my $localVarType (qw/arg localVar/) {
            my $foundVarType = $currentCodeGearInfo->{$localVarType}->{$arg};
            if ($foundVarType && $foundVarType eq '__code') {
              $hasGotoArgOrLocalVar = 1;
            }
          }

          # inteface case

          if ($arg =~ /->/) {
            print $fd "${indent}Gearef(${context_name}, $ntype)->$pName = $arg;\n"; #Gearef->()->next = bar->baz;
            $i++;
            next;
          }

          if ($hasGotoArgOrLocalVar) {
            print $fd "${indent}Gearef(${context_name}, $ntype)->$pName = $arg;\n"; #Gearef->()->next = next;
            $i++;
            next;
          }

          print $fd "${indent}Gearef(${context_name}, $ntype)->$pName = C_$arg;\n";
          $i++;
          next;
      }

# stubの生成

  • generateStubの最後に行う
  • 既に__stubの中に何を書けばいいのかは確定している
    • 置き換える予定のフィールドを取得して、置換して解決
  • 名前は出てきた順に hoge_1_stubと_\dがつく
  • 今の所静的にstubがあれば生成しない
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
    #Create a stub when the output is a different interface
    for my $modifyEnumCodeCodeGear (keys %{$generateHaveOutputStub->{list}})  {
      my $outputStubElem      = $generateHaveOutputStub->{list}->{$modifyEnumCodeCodeGear};
      my $targetStubName      = $outputStubElem->{createStubName};
      my $createStubName      = "$outputStubElem->{createStubName}_$outputStubElem->{counter}";
      my $replaceArgs         = $outputStubElem->{args};
      my $replaceStubContents = $dataGearName{$targetStubName};

      #If the stub was handwritten, skip
      if ($stub{"${targetStubName}_stub"}->{static}) {
        next;
      }

      for my $arg (keys %$replaceArgs) {
        my $interface = $replaceArgs->{$arg};
        $replaceStubContents =~ s/,(.*)\)->$arg/,$interface)->$arg/;
      }

      generateStub($fd,$createStubName,$replaceStubContents);
    }

# まだやってない実装

  • 他のファイルで定義しているCodeGearに継続するケース
    • 継続先の引数を読みに行って生成すれば問題ないのでできそう
      • 既にCodeGearの名前から.cbcを特定する処理は実装済み
    • 名前は工夫する必要がある
  • Intefaceの実装に継続するケース
    • goto hoge->(stack->pop)
    • Stackを実装しているImplの全部のpopに対してstubを生成
    • なんとかして切り替える

# ほか

  • Gearefのインデントが揃うようになりました
    • tabで頑張ってた所をgoto hoge();のgotoまでのインデントに揃えるようにした
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
__code odgCommitCPUWorker(struct Context *context,struct CPUWorker* worker, struct Context* task) {
    if (task->iterate) {
        struct Iterator* iterator = task->iterator;
        Gearef(context, Iterator)->iterator = (union Data*) iterator;
        Gearef(context, Iterator)->task = task;
        Gearef(context, Iterator)->next = C_odgCommitCPUWorker1;
        Gearef(context, Iterator)->whenWait = C_odgCommitCPUWorker6;
        context->before = C_odgCommitCPUWorker;
        goto meta(context, iterator->barrier);
    } else {
        context->before = C_odgCommitCPUWorker;
        goto meta(context, C_odgCommitCPUWorker1);
    }
}

# 問題点

# 直接gotoで指定すると引数が落とされる

  • こうかくと
1
2
3
4
5
6
7
__code pop2Test3_StackTestImpl(struct StackTestImpl* stackTest, struct Stack* stack, __code next(...)) {
  String* str = NEW(String);
  str->size = 200;
  String* str1 = NEW(String);
  str1->size = 300;
  goto pop2Test1_StackTestImpl(stackTest, (union Data*)str, (union Data*)str1);
}
  • こうなってしまう
    • (union Data*)strとかが落とされる…
1
2
3
4
5
6
7
__code pop2Test3_StackTestImpl(struct Context *context,struct StackTestImpl* stackTest, struct Stack* stack, enum Code next) {
  String* str = NEW(String);
  str->size = 200;
  String* str1 = NEW(String);
  str1->size = 300;
  goto meta(context, C_pop2Test1_StackTestImpl);
}

# 今週

  • Gearsに名前空間の実装
  • シス管
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy