GoのAST全部見る

この記事は Treasure Advent Calendar 2018 - Qiita 16日目の記事です.

クリスマスにちなんで,プログラマ御用達の大木 Abstract Syntax Treeを眺めようと思います.

うまれたてのGopherなので, 間違ってたら優しく教えてください~~

go.astパッケージ

GoはGo自身をParseしてASTに変換するgo.astパッケージを搭載していて, お手軽にASTを眺めることができます.

例えば以下のコードのParse()関数に

package parser

import (
    "fmt"
    "go/ast"
    "go/parser"
    "go/token"
)

func Parse(filename string) error {
    fset := token.NewFileSet()
    f, err := parser.ParseFile(fset, filename, nil, parser.Mode(0))

    if err != nil {
        return err
    }

    for _, d := range f.Decls {
        ast.Print(fset, d)
        fmt.Println() // \n したい...
    }
    return nil
}

以下の簡単なGoプログラムを渡すと

package main

func MerryXMas() {
    var hoge = 2
}

それらしいASTが出力されます.

     0  *ast.FuncDecl {
     1  .  Name: *ast.Ident {
     2  .  .  NamePos: example/example.go:3:6
     3  .  .  Name: "MerryXMas"
     4  .  .  Obj: *ast.Object {
     5  .  .  .  Kind: func
     6  .  .  .  Name: "MerryXMas"
     7  .  .  .  Decl: *(obj @ 0)
     8  .  .  }
     9  .  }
    10  .  Type: *ast.FuncType {
    11  .  .  Func: example/example.go:3:1
    12  .  .  Params: *ast.FieldList {
    13  .  .  .  Opening: example/example.go:3:15
    14  .  .  .  Closing: example/example.go:3:16
    15  .  .  }
    16  .  }
    17  .  Body: *ast.BlockStmt {
    18  .  .  Lbrace: example/example.go:3:18
    19  .  .  List: []ast.Stmt (len = 1) {
    20  .  .  .  0: *ast.DeclStmt {
    21  .  .  .  .  Decl: *ast.GenDecl {
    22  .  .  .  .  .  TokPos: example/example.go:4:2
    23  .  .  .  .  .  Tok: var
    24  .  .  .  .  .  Lparen: -
    25  .  .  .  .  .  Specs: []ast.Spec (len = 1) {
    26  .  .  .  .  .  .  0: *ast.ValueSpec {
    27  .  .  .  .  .  .  .  Names: []*ast.Ident (len = 1) {
    28  .  .  .  .  .  .  .  .  0: *ast.Ident {
    29  .  .  .  .  .  .  .  .  .  NamePos: example/example.go:4:6
    30  .  .  .  .  .  .  .  .  .  Name: "hoge"
    31  .  .  .  .  .  .  .  .  .  Obj: *ast.Object {
    32  .  .  .  .  .  .  .  .  .  .  Kind: var
    33  .  .  .  .  .  .  .  .  .  .  Name: "hoge"
    34  .  .  .  .  .  .  .  .  .  .  Decl: *(obj @ 26)
    35  .  .  .  .  .  .  .  .  .  .  Data: 0
    36  .  .  .  .  .  .  .  .  .  }
    37  .  .  .  .  .  .  .  .  }
    38  .  .  .  .  .  .  .  }
    39  .  .  .  .  .  .  .  Values: []ast.Expr (len = 1) {
    40  .  .  .  .  .  .  .  .  0: *ast.BasicLit {
    41  .  .  .  .  .  .  .  .  .  ValuePos: example/example.go:4:13
    42  .  .  .  .  .  .  .  .  .  Kind: INT
    43  .  .  .  .  .  .  .  .  .  Value: "2"
    44  .  .  .  .  .  .  .  .  }
    45  .  .  .  .  .  .  .  }
    46  .  .  .  .  .  .  }
    47  .  .  .  .  .  }
    48  .  .  .  .  .  Rparen: -
    49  .  .  .  .  }
    50  .  .  .  }
    51  .  .  }
    52  .  .  Rbrace: example/example.go:5:1
    53  .  }
    54  }

ASTを眺めていると分かる通り, go.astパッケージはたくさんの構造体でASTを表現しています.今回は,go.astパッケージで実装されている構造体を可能な限り見ていきたいと思います.

構造体の種類

構造体は大きく6種類に分類できます(主観).

  • Declaration : 関数/変数の宣言の外枠
  • Spec : 関数/変数宣言の詳細(Spec)
  • Type : 型情報
  • Statement : ブロックや式情報
  • Literal : 関数リテラル(無名関数) , 整数リテラル(つまり数字)などの即時情報
  • Expression : 評価が必要な式情報 (1+1, !true など)

この他にも, たまに分類不能な雑多な構造体が見受けられます.

皆さんには今日これらを全部?見てもらいます😇

!Notion!

構造体を見ていく(本記事を読む)上での前提をいくつか記述します.

Position

AST情報の一つとして, Positionという言葉が頻出します. これは{typeといったキーワードが .goファイル内のどの位置に出現したものかを記録しています.フォーマットは

example/sampleAstProgram.go:7:11

の形式で, sampleAstProgram.goファイルの7行11文字にキーワードが存在することを表しています. これによって基本ブロックがどこからどこまでか( ≒ {}の位置) 等をコンパイラが把握し, エラー行を詳細に表示することができます.

以降では構造体内のPositionを格納するメンバに関する説明を省略しています.

Comment

Javaのように, 関数や変数に付加されたコメントを格納する構造体メンバも存在します. こちらも説明を省略します.

Bad[Decl, Expr, Stmt]

Parse中にSyntaxErrorが発生し構造体を生成できない場合, 一旦BadDecl, BadExprといった構造体が生成されるようです.

メタ構造体たち

読み飛ばせばいいやつです.

File

1つのGoファイル全体の情報を持つ構造体です.

type File struct {
        Doc        *CommentGroup   // associated documentation; or nil
        Package    token.Pos       // position of "package" keyword
        Name       *Ident          // package name
        Decls      []Decl          // top-level declarations; or nil
        Scope      *Scope          // package scope (this file only)
        Imports    []*ImportSpec   // imports in this file
        Unresolved []*Ident        // unresolved identifiers in this file
        Comments   []*CommentGroup // list of all comments in the source file
}

Ident

変数, 関数名と, そのオブジェクトのASTのリンカの役割です.

type Ident struct {
        NamePos token.Pos // identifier position
        Name    string    // identifier name
        Obj     *Object   // denoted object; or nil
}

Object

変数, 関数など, オブジェクト1つのAST情報などを保存するための構造体です.Identから引くことができます.

type Object struct {
        Kind ObjKind
        Name string      // declared name
        Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, AssignStmt, Scope; or nil
        Data interface{} // object-specific data; or nil
        Type interface{} // placeholder for type information; may be nil
}
  • Kind
   const (
            Bad ObjKind = iota // for error handling
            Pkg                // package
            Con                // constant
            Typ                // type
            Var                // variable
            Fun                // function or method
            Lbl                // label
    )
  • Name : Ident構造体のメンバNameと一致します.

ここから本題に入っていきます.長いです.あと文体が変わります.

説明順の関係で, どうしても後方参照になってしまっている構造体のメンバがあります. 知らない構造体名が出てきても気にせず進む方が早いと思います.

Declaration

関数, 変数宣言など

GenDecl

import, const, type or var による宣言文

type GenDecl struct {
    Doc    *CommentGroup // associated documentation; or nil
    TokPos token.Pos     // position of Tok
    Tok    token.Token   // IMPORT, CONST, TYPE, VAR
    Lparen token.Pos     // position of '(', if any
    Specs  []Spec
    Rparen token.Pos // position of ')', if any
}
  • Tok : 宣言するオブジェクトのタイプ
    • token.IMPORT import "fmt"
    • token.CONST const string := "hoge"
    • token.VAR var variable int
    • token.TYPE type Hex int
  • Specs : オブジェクトの詳細情報. see ImportSpec, ValueSpec, and TypeSpec

FuncDecl

func による関数宣言

type FuncDecl struct {
    Doc  *CommentGroup // associated documentation; or nil
    Recv *FieldList    // receiver (methods); or nil (functions)
    Name *Ident        // function/method name
    Type *FuncType     // function signature: parameters, results, and position of "func" keyword
    Body *BlockStmt    // function body; or nil for external (non-Go) function
}
  • Recv : メソッドfunc (*struct) funcName(){} の場合, struct部分の情報が入る. Listってことは複数書ける...?
  • Name: 関数名
  • Type : どちらもList
    • Params : 引数
    • Results : 返り値
  • Body : 関数のBody部分. see Stmt

Spec

オブジェクトの詳細情報

ImportSpec

import文に関する情報

type ImportSpec struct {
        Doc     *CommentGroup // associated documentation; or nil
        Name    *Ident        // local package name (including "."); or nil
        Path    *BasicLit     // import path
        Comment *CommentGroup // line comments; or nil
        EndPos  token.Pos     // end of spec (overrides Path.Pos if nonzero)
}
  • Name : ローカルパッケージをimportすると, その名前が入ると書いてあるが, "./localpackage"をimportしてもnilのまま😔
  • Path : BasicLit
    • Kind : パスとして指定されるリテラル. おそらく"STRING"以外入らないのではないか.
    • Value : "\"fmt\""など.

ValueSpec

var, constによるフィールド宣言

type ValueSpec struct {
    Doc     *CommentGroup // associated documentation; or nil
    Names   []*Ident      // value names (len(Names) > 0)
    Type    Expr          // value type; or nil
    Values  []Expr        // initial values; or nil
    Comment *CommentGroup // line comments; or nil
}
  • Names : フィールド名. var x1, x2 int -> {x1, x2}` のように, 複数入る
  • Type : int等の情報が入る. 主に Ident構造体.
  • Values : 式を表すExprや数字, 文字を表すLiteral構造体によるValue定義が入る

TypeSpec

typeによる型宣言

type TypeSpec struct {
        Doc     *CommentGroup // associated documentation; or nil
        Name    *Ident        // type name
        Assign  token.Pos     // position of '=', if any; added in Go 1.9
        Type    Expr          // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
        Comment *CommentGroup // line comments; or nil
}
  • Assign : Type Aliasを使用している場合, alias元の型名が入る.
// example: Type Alias
    type myInt int
    type myIntAlias = fuga
    type myInt2 int
    var x1 myInt = 2
    var x2 myIntAlias = x1 // OK
    var x3 myInt2 = x1 // NG
  • Type 宣言される型の種類
    • int -> ast.Ident
    • func(...){...} -> ast.FuncType
    • struct{...} -> ast.StructType
    • type hoge = fuga -> ast.Ident

Type

型情報.

FuncType

関数型の情報. 引数と返り値情報のみで, method情報は含まれない.

type FuncType struct {
        Func    token.Pos  // position of "func" keyword (token.NoPos if there is no "func")
        Params  *FieldList // (incoming) parameters; non-nil
        Results *FieldList // (outgoing) results; or nil
}

StructType

構造体の情報.Incomplete boolをtrueにする条件がわからなかった><

type StructType struct {
        Struct     token.Pos  // position of "struct" keyword
        Fields     *FieldList // list of field declarations
        Incomplete bool       // true if (source) fields are missing in the Fields list
}
  • Fields : 構造体のメンバフィールドの情報が入る

InterfaceType

インターフェースの情報

type InterfaceType struct {
        Interface  token.Pos  // position of "interface" keyword
        Methods    *FieldList // list of methods
        Incomplete bool       // true if (source) methods are missing in the Methods list
}
  • Methods : インターフェース内で宣言されているメソッド(関数)の情報が入る

ArrayType

array, sliceの情報

type ArrayType struct {
        Lbrack token.Pos // position of "["
        Len    Expr      // Ellipsis node for [...]T array types, nil for slice types
        Elt    Expr      // element type
}
  • Len
    • []int -> nil
    • [3]int -> "3"(BasicLit構造体)
  • Elt
    • []int -> int(Ident)

MapType

mapの情報

type MapType struct {
        Map   token.Pos // position of "map" keyword
        Key   Expr
        Value Expr
}
  • Key : map[string]int -> string(Ident構造体)
  • Value : map[string]int -> int(Ident)

ChanType

channelの情報

type ChanType struct {
        Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
        Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-"); added in Go 1.1
        Dir   ChanDir   // channel direction
        Value Expr      // value type
}
  • Dir : 下記の通り, 1か2が入りそうな雰囲気だが3が出た.わからん.
   const (
            SEND ChanDir = 1 << iota
            RECV
    )
  • Value : c chan int -> Ident int

    Statement

BlockStmt

{} で囲われたblock全体を表す. blockの中身を, Stmtの配列として持つ.

type BlockStmt struct {
        Lbrace token.Pos // position of "{"
        List   []Stmt
        Rbrace token.Pos // position of "}"
}

ExprStmt

評価式を表す.

type ExprStmt struct {
        X Expr // expression
}

DeclStmt

関数内でのフィールド宣言を表す. ただし:=はAssignStmtで管理される.

グローバル変数の場合はGenDeclが直接呼ばれる.

type DeclStmt struct {
        Decl Decl // *GenDecl with CONST, TYPE, or VAR token
}

AssignStmt

:=を用いた代入文を表す.

type AssignStmt struct {
    Lhs    []Expr
    TokPos token.Pos   // position of Tok
    Tok    token.Token // assignment token, DEFINE
    Rhs    []Expr
}
  • Lhs,Rhs : c := 4 において, Lhsにはc, Rhsには4の情報が入る
  • Tok : :=が入る

ReturnStmt

return文を表す.

type ReturnStmt struct {
    Return  token.Pos // position of "return" keyword
    Results []Expr    // result expressions; or nil
}
  • Results :
    • return 1 -> 1(BasicLit構造体)
    • return -> nil

IncDecStmt

インクリメントi++ やデクリメントi--を表す.

type IncDecStmt struct {
        X      Expr
        TokPos token.Pos   // position of Tok
        Tok    token.Token // INC or DEC
}
  • X : インクリメント(デクリメント)する変数
  • Tok : ++ or --

ForStmt

for 文を表す.ただしfor range文を除く. 無限ループ for{}のときは, Init,Cond,Postの全てがnilになる.

type ForStmt struct {
        For  token.Pos // position of "for" keyword
        Init Stmt      // initialization statement; or nil
        Cond Expr      // condition; or nil
        Post Stmt      // post iteration statement; or nil
        Body *BlockStmt
}
  • Init
    • for i := 0; i < 10; i++ {} -> i := 0( AssignStmt構造体 )
  • Cond
    • for i := 0; i < 10; i++ {} -> i < 10(BinaryExpr)
    • for coundown > 0 {} -> coundown > 0(BinaryExpr)
  • Post
    • for i := 0; i < 10; i++ {} -> i++(IncDecStmt)
  • Body : {}の中身

RangeStmt

for range文を表す.

type RangeStmt struct {
        For        token.Pos   // position of "for" keyword
        Key, Value Expr        // Key, Value may be nil
        TokPos     token.Pos   // position of Tok; invalid if Key == nil
        Tok        token.Token // ILLEGAL if Key == nil, ASSIGN, DEFINE
        X          Expr        // value to range over
        Body       *BlockStmt
}
  • Key, Value : for i, v := range arr {}のASTは次のようになる.

Keyとしてiの情報を得るためには arr(v)の情報が必要であり, どうやら Keyの時点で全ての情報を展開しているようだ. その後ValueXでは, Keyで展開した情報を参照している. Obj: *(obj @ 131)は, ASTの131行目のオブジェクトを参照している雰囲気がある.

    189  .  .  .  .  Key: *ast.Ident {
       190  .  .  .  .  .  NamePos: example/stmt.go:14:6
       191  .  .  .  .  .  Name: "i"
       192  .  .  .  .  .  Obj: *ast.Object {
       193  .  .  .  .  .  .  Kind: var
       194  .  .  .  .  .  .  Name: "i"
       195  .  .  .  .  .  .  Decl: *ast.AssignStmt {
       196  .  .  .  .  .  .  .  Lhs: []ast.Expr (len = 2) {
       197  .  .  .  .  .  .  .  .  0: *(obj @ 189)
       198  .  .  .  .  .  .  .  .  1: *ast.Ident {
       199  .  .  .  .  .  .  .  .  .  NamePos: example/stmt.go:14:9
       200  .  .  .  .  .  .  .  .  .  Name: "s"
       201  .  .  .  .  .  .  .  .  .  Obj: *ast.Object {
       202  .  .  .  .  .  .  .  .  .  .  Kind: var
       203  .  .  .  .  .  .  .  .  .  .  Name: "s"
       204  .  .  .  .  .  .  .  .  .  .  Decl: *(obj @ 195)
       205  .  .  .  .  .  .  .  .  .  }
       206  .  .  .  .  .  .  .  .  }
       207  .  .  .  .  .  .  .  }
       208  .  .  .  .  .  .  .  TokPos: example/stmt.go:14:11
       209  .  .  .  .  .  .  .  Tok: :=
       210  .  .  .  .  .  .  .  Rhs: []ast.Expr (len = 1) {
       211  .  .  .  .  .  .  .  .  0: *ast.UnaryExpr {
       212  .  .  .  .  .  .  .  .  .  OpPos: example/stmt.go:14:14
       213  .  .  .  .  .  .  .  .  .  Op: range
       214  .  .  .  .  .  .  .  .  .  X: *ast.Ident {
       215  .  .  .  .  .  .  .  .  .  .  NamePos: example/stmt.go:14:20
       216  .  .  .  .  .  .  .  .  .  .  Name: "strArray"
       217  .  .  .  .  .  .  .  .  .  .  Obj: *(obj @ 131)
       218  .  .  .  .  .  .  .  .  .  }
       219  .  .  .  .  .  .  .  .  }
       220  .  .  .  .  .  .  .  }
       221  .  .  .  .  .  .  }
       222  .  .  .  .  .  }
       223  .  .  .  .  }
       224  .  .  .  .  Value: *(obj @ 198)
  • Tok ::=など
  • X : for i, v := range arr{} -> arr (ast.Ident構造体 -> UnaryExpr構造体)
  • Body : {}の中身

IfStmt

if, if-else文を表す. if-else if文は, IfStmt再帰的に呼び出して生成される.

type IfStmt struct {
        If   token.Pos // position of "if" keyword
        Init Stmt      // initialization statement; or nil
        Cond Expr      // condition
        Body *BlockStmt
        Else Stmt // else branch; or nil
}
  • Init : if a := true; a {} -> a := true;(AssignStmt構造体)
  • Cond
    • if a > 3 {} -> a > 3 (BinaryExpr)
    • if a := true; a {} -> a(Ident)
  • Body : {}の中身
  • Else
    • if a {} else {...} ->{...}( BlockStmt )
    • if a {} else if {...} -> if {...}(IfStmt)

SwitchStmt

switch{} 文を表す.

type SwitchStmt struct {
    Switch token.Pos  // position of "switch" keyword
    Init   Stmt       // initialization statement; or nil
    Tag    Expr       // tag expression; or nil
    Body   *BlockStmt // CaseClauses only
}
  • Init : switch i := 1; i{} -> i := 1(AssignStmt構造体)
  • Tag :
    • switch i {} -> i(Ident)
    • switch {} -> nil
  • Body : inner {} CaseClause構造体によって, case文が管理される.
     type CaseClause struct {
            Case  token.Pos // position of "case" or "default" keyword
            List  []Expr    // list of expressions or types; nil means default case
            Colon token.Pos // position of ":"
            Body  []Stmt    // statement list; or nil
        }
        - List :
            - `case 1,2,3:` -> `1,2,3` ([]BasicLit)
            - `default` ->  nil

TypeSwitchStmt

type switch文と呼ばれる, switch文の条件に型を使用するswitch文のstatement.

// example of Type Switch
    switch v := value.(type) {
        case nil:
        case int:
        default:
    }
type TypeSwitchStmt struct {
        Switch token.Pos  // position of "switch" keyword
        Init   Stmt       // initialization statement; or nil
        Assign Stmt       // x := y.(type) or y.(type)
        Body   *BlockStmt // CaseClauses only
}
  • Init : SwitchStmtと同じ
  • Assign : v := value.(type) -> AssignStmt -> (Ident := TypeAssertExpr)
  • Body : {}の中身. SwitchStmt`と同じ

LabeledStmt

JUMP:のようなラベルと, その直後の1つのstatementブロックを表す switch文における case 1:のようなラベルは対象外.

type LabeledStmt struct {
        Label *Ident
        Colon token.Pos // position of ":"
        Stmt  Stmt
}
  • Label :
   44  .  .  .  1: *ast.LabeledStmt {
    45  .  .  .  .  Label: *ast.Ident {
    46  .  .  .  .  .  NamePos: example/label-stmt.go:7:1
    47  .  .  .  .  .  Name: "FOR_LABEL"
    48  .  .  .  .  .  Obj: *ast.Object {
    49  .  .  .  .  .  .  Kind: label
    50  .  .  .  .  .  .  Name: "FOR_LABEL"
    51  .  .  .  .  .  .  Decl: *(obj @ 44)
    52  .  .  .  .  .  }
    53  .  .  .  .  }
  • Stmt : ラベルの直後のstatementが入る.下の例では, fmt.Println("hello")が当てはまる.
   FOR_LABEL:
        fmt.Println("hello")
        fmt.Println("world")

BranchStmt

break, continue, goto, or fallthrough キーワードを含むstatement.

fallthroughはswitch-case文の中で使われ,現在の次のcase項の先頭(条件比較式)にジャンプできる.つまり, cace文内で自動breakされるのを回避することができる.

goto文以外にも, break, continue文もラベルを引数に取ることができる.この場合, そのラベル直後のstatementから処理を再開する.

type BranchStmt struct {
        TokPos token.Pos   // position of Tok
        Tok    token.Token // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH)
        Label  *Ident      // label name; or nil
}
  • Tok
    • break [LABEL_NAME]
    • continue [LABEL_NAME]
    • goto LABEL_NAME
    • fallthrough : switch文の中で使われ,
  • Label
    • break : ラベルが付加されているstatementはForStmt, SwitchStmt, SelectStmtいづれかでなければならない
    • continue : ラベルが付加されているstatementはForStmtでなければならない.

DeferStmt

defer文を表す.

type DeferStmt struct {
        Defer token.Pos // position of "defer" keyword
        Call  *CallExpr
}
  • Call : 呼び出される関数

GoStmt

goキーワードを用いた並列処理文を表す.

type GoStmt struct {
        Go   token.Pos // position of "go" keyword
        Call *CallExpr
}
  • Call : 呼び出される関数

SelectStmt

channelのselect文を表す.

   select {
    case ch <- v:
    default:
    }
type SelectStmt struct {
        Select token.Pos  // position of "select" keyword
        Body   *BlockStmt // CommClauses only
}
  • Body : CommClauses構造体で表される.
   type CommClause struct {
        Case  token.Pos // position of "case" or "default" keyword
        Comm  Stmt      // send or receive statement; nil means default case
        Colon token.Pos // position of ":"
        Body  []Stmt    // statement list; or nil
    }
- Comm : 
    - `case ch <- v:` -> SendStmt
    - `case a := <- ch:` -> AssignStmt -> (Ident := UnaryExpr)
    - `case 2:` -> ExprStmt. syntax errorにはならなかったが, semantics errorか否かは試してない
    - switch-case文のように, caseの後に複数条件を書くことはできない
- Body : caseに対応する処理

SendStmt

channelへのメッセージ送信 ch <- aを表す.

type SendStmt struct {
        Chan  Expr
        Arrow token.Pos // position of "<-"
        Value Expr
}
  • Chan : ch <- a -> ch(Ident)
  • Value : ch <- a -> a(Ident)

EmptyStmt

nop! 不要な;を除去するためにある...?

type EmptyStmt struct {
        Semicolon token.Pos // position of following ";"
        Implicit  bool      // if set, ";" was omitted in the source; added in Go 1.5
}

Literal

BasicLit

Integer, Float, Imaginary(虚数), Char, String literalを表す.

type BasicLit struct {
        ValuePos token.Pos   // literal position
        Kind     token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
        Value    string      // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}

CompositeLit

Composite literalsを表す. 例えば以下のような初期化時代入のものをいう.

/// example of composite literals
type fuga struct {
    a int
    b string
}

var fooo = fuga{1} // here

var arr = []int{1, 2, 3} // here
type CompositeLit struct {
        Type       Expr      // literal type; or nil
        Lbrace     token.Pos // position of "{"
        Elts       []Expr    // list of composite elements; or nil
        Rbrace     token.Pos // position of "}"
        Incomplete bool      // true if (source) expressions are missing in the Elts list; added in Go 1.11
}
  • Type :構造体や配列の型情報
  • Elts : var fooo = fuga{1} -> 1(BasicLit)

FuncLit

関数リテラル(無名関数)を表す.

// example of function literal
var f = func(src string) string { return "<<" + src + ">>" }
type FuncLit struct {
        Type *FuncType  // function type
        Body *BlockStmt // function body
}

Expression

評価が必要な値を表す.

BinaryExpr

1+3, x == 2

type BinaryExpr struct {
    X     Expr        // left operand
    OpPos token.Pos   // position of Op
    Op    token.Token // operator
    Y     Expr        // right operand
}

CallExpr

func(1,2)

type CallExpr struct {
    Fun      Expr      // function expression
    Lparen   token.Pos // position of "("
    Args     []Expr    // function arguments; or nil
    Ellipsis token.Pos // position of "..." (token.NoPos if there is no "...")
    Rparen   token.Pos // position of ")"
}

IndexExpr

arr[2]

type IndexExpr struct {
    X      Expr      // expression
    Lbrack token.Pos // position of "["
    Index  Expr      // index expression
    Rbrack token.Pos // position of "]"
}

KeyValueExpr

var s = MyStruct{key: "value"}

type KeyValueExpr struct {
        Key   Expr
        Colon token.Pos // position of ":"
        Value Expr
}

ParenExpr

(1+2)/3

type ParenExpr struct {
    Lparen token.Pos // position of "("
    X      Expr      // parenthesized expression
    Rparen token.Pos // position of ")"
}

SelectorExpr

fmt.Println("")

type SelectorExpr struct {
        X   Expr   // expression
        Sel *Ident // field selector
}
  • X : fmt.Println("🍣") -> "fmt"
  • Sel : fmt.Println("🍖") -> "Println"

SliceExpr

c := a[2:4]

type SliceExpr struct {
        X      Expr      // expression
        Lbrack token.Pos // position of "["
        Low    Expr      // begin of slice range; or nil
        High   Expr      // end of slice range; or nil
        Max    Expr      // maximum capacity of slice; or nil; added in Go 1.2
        Slice3 bool      // true if 3-index slice (2 colons present); added in Go 1.2
        Rbrack token.Pos // position of "]"
}
  • X : c := a[2:4] -> a (Ident構造体)
  • Low : c := a[2:4] -> 2 (BasicLit)
  • High : c := a[2:4] -> 4 (BasicLit)
  • Max : c := a[2:4:3] -> 3 (BasicLit)
  • Slice3 : sliceはa[low : high : max]のようにmaxを指定することができる. 指定していればtrue.

StarExpr

*Hoge

type StarExpr struct {
        Star token.Pos // position of "*"
        X    Expr      // operand
}
  • Star : position
  • X : *Hoge -> Expr of Hoge (ex, CompositeLit)

TypeAssertExpr

3.00.(int)

type TypeAssertExpr struct {
        X      Expr      // expression
        Lparen token.Pos // position of "("; added in Go 1.2
        Type   Expr      // asserted type; nil means type switch X.(type)
        Rparen token.Pos // position of ")"; added in Go 1.2
}
  • X : 3.00.(int) -> 3.00(FLOAT) (BasicLit構造体)
  • Type :3.00.(int) ->int ( Ident )

UnaryExpr

ポインタ* (StarExpr)を除く単項演算子による評価を表す.

(ex, !true, var b = &a, ^2)

type UnaryExpr struct {
        OpPos token.Pos   // position of Op
        Op    token.Token // operator
        X     Expr        // operand
}
  • Op : &a -> "&"
  • X : &a -> a (Ident 構造体)

その他

どうしても分類できなかったもの

Field, FieldList

実は今までたくさん出てきたField構造体. フィールド情報を表す.

type Field struct {
        Doc     *CommentGroup // associated documentation; or nil
        Names   []*Ident      // field/method/parameter names; or nil
        Type    Expr          // field/method/parameter type
        Tag     *BasicLit     // field tag; or nil
        Comment *CommentGroup // line comments; or nil
}

type FieldList struct {
        Opening token.Pos // position of opening parenthesis/brace, if any
        List    []*Field  // field list; or nil
        Closing token.Pos // position of closing parenthesis/brace, if any
}

Ellipsis

可変長引数, 配列を表す... <- これ

type Ellipsis struct {
        Ellipsis token.Pos // position of "..."
        Elt      Expr      // ellipsis element type (parameter lists only); or nil
}

見逃していなければ, go.astによって生成されるAST構造体はこれで全部のはず...?お疲れ様でした.

まとめ

書いてて思ったけど誰が読むんですかねこれ. 読ませる気がないですね... 自分用メモは こちら

ASTを眺めると, 知らなかった書き方(というかGoの全記法)を知れて良いですね. 一回読むだけで, 書き方でググる機会も減る気がします.

さて, ASTがだいたい判明したので, 誰かGoコンパイラ読んでみた記事書いてくれませんか?協力します☀️

structs in go.ast package

See also https://golang.org/pkg/go/ast.

Declaration

GenDecl

import, constant, type or variable declaration.

type GenDecl struct {
    Doc    *CommentGroup // associated documentation; or nil
    TokPos token.Pos     // position of Tok
    Tok    token.Token   // IMPORT, CONST, TYPE, VAR
    Lparen token.Pos     // position of '(', if any
    Specs  []Spec
    Rparen token.Pos // position of ')', if any
}
  • TokPos : file path, line, column. ex) example/example.go:3:1
  • Tok : type of def
    • token.IMPORT import "fmt"
    • token.CONST const constant string := "hoge"
    • token.VAR var variable int
    • token.TYPE type Hex int
  • Lparen, Rparen : see later.
  • Specs : for any of ImportSpec, ValueSpec, and TypeSpec

FuncDecl

function declaration using func

type FuncDecl struct {
    Doc  *CommentGroup // associated documentation; or nil
    Recv *FieldList    // receiver (methods); or nil (functions)
    Name *Ident        // function/method name
    Type *FuncType     // function signature: parameters, results, and position of "func" keyword
    Body *BlockStmt    // function body; or nil for external (non-Go) function
}
  • Recv : field (almost struct) infomation if methods, func (*struct) funcName(){}
  • Name: func name (type ast.Ident)
  • Type : params and return fields
    • Params : ast.FieldList (position and ast.Field)
    • Results : position and ast.Field
  • Body : function body:: main process of this func. see BlockStmt later

Spec

detail spec of import, variables, type. used by GenDecl

ImportSpec

type ImportSpec struct {
        Doc     *CommentGroup // associated documentation; or nil
        Name    *Ident        // local package name (including "."); or nil
        Path    *BasicLit     // import path
        Comment *CommentGroup // line comments; or nil
        EndPos  token.Pos     // end of spec (overrides Path.Pos if nonzero)
}
  • Name : I cannot detect when this field isnt nil. Evenif use "./localpackage", never appeared:(
  • Path : BasicLit
    • ValuePos : position this package included
    • Kind : "STRING" (I think importSpec is to need this Kind: STRING)
    • Value : string. like "\"fmt\""

ValueSpec

constant or variable declaration using var, const.

type ValueSpec struct {
    Doc     *CommentGroup // associated documentation; or nil
    Names   []*Ident      // value names (len(Names) > 0)
    Type    Expr          // value type; or nil
    Values  []Expr        // initial values; or nil
    Comment *CommentGroup // line comments; or nil
}
  • Names : value names ex) var var1, var2 int -> {var1, var2}`
  • Type : ex) ast.Ident *ast.Ident { NamePos: example/example.go:3:16 Name: "int"
  • Values : Expr(BinaryExpr, BasicLit, ...)

TypeSpec

represents a Type declaration

type TypeSpec struct {
        Doc     *CommentGroup // associated documentation; or nil
        Name    *Ident        // type name
        Assign  token.Pos     // position of '=', if any; added in Go 1.9
        Type    Expr          // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
        Comment *CommentGroup // line comments; or nil
}
  • Assign : Type Alias. position of = if exists.
   type fuga int
    type hoge = fuga
    var x1 fuga = 2
    var x2 hoge = x1 // ok
  • Type
    • int : ast.Ident
    • func(...){...} : ast.FuncType
    • struct{...} : ast.StructType
    • type hoge = fuga : ast.Ident
    • TODO ParenExpr, SelectorExpr, StarExpr

Stmt

BlockStmt

{} block.

type BlockStmt struct {
        Lbrace token.Pos // position of "{"
        List   []Stmt
        Rbrace token.Pos // position of "}"
}

ExprStmt

expression. TODO see Expr later.

type ExprStmt struct {
        X Expr // expression
}

DeclStmt

declaration in a function as follow.

func main () {
    const a = 3
}
type DeclStmt struct {
        Decl Decl // *GenDecl with CONST, TYPE, or VAR token
}

AssignStmt

assignment or a short variable declaration using :=.

type AssignStmt struct {
    Lhs    []Expr
    TokPos token.Pos   // position of Tok
    Tok    token.Token // assignment token, DEFINE
    Rhs    []Expr
}
  • Lhs,Rhs : (left) c := 4 (right)
  • Tok : :=, other hasnt been found for me.

ReturnStmt

return statement.

type ReturnStmt struct {
    Return  token.Pos // position of "return" keyword
    Results []Expr    // result expressions; or nil
}
  • Return : position
  • Result :
    • return 1 : Expr[] -> BasicLit 1
    • return : nil

IncDecStmt

i++ or i-- statements.

type IncDecStmt struct {
        X      Expr
        TokPos token.Pos   // position of Tok
        Tok    token.Token // INC or DEC
}
  • X : target variable.
  • Tok : ++ or --

ForStmt

for statement exept for range.

When for {} (infinity), all of three Init, Cond, Post are nil.

type ForStmt struct {
        For  token.Pos // position of "for" keyword
        Init Stmt      // initialization statement; or nil
        Cond Expr      // condition; or nil
        Post Stmt      // post iteration statement; or nil
        Body *BlockStmt
}
  • For : posision
  • Init
    • for i := 0; i < 10; i++ {} : AssignStmt i := 0
  • Cond
    • for i := 0; i < 10; i++ {} : BinaryExpr i < 10
    • for coundown > 0 {} : BinaryExpr coundown > 0
  • Post
    • for i := 0; i < 10; i++ {} : IncDecStmt i++
  • Body : inner {}

RangeStmt

for range statement

type RangeStmt struct {
        For        token.Pos   // position of "for" keyword
        Key, Value Expr        // Key, Value may be nil
        TokPos     token.Pos   // position of Tok; invalid if Key == nil
        Tok        token.Token // ILLEGAL if Key == nil, ASSIGN, DEFINE
        X          Expr        // value to range over
        Body       *BlockStmt
}
  • For : position
  • Key, Value : for i, v := range arr {}
    189  .  .  .  .  Key: *ast.Ident {
       190  .  .  .  .  .  NamePos: example/stmt.go:14:6
       191  .  .  .  .  .  Name: "i"
       192  .  .  .  .  .  Obj: *ast.Object {
       193  .  .  .  .  .  .  Kind: var
       194  .  .  .  .  .  .  Name: "i"
       195  .  .  .  .  .  .  Decl: *ast.AssignStmt {
       196  .  .  .  .  .  .  .  Lhs: []ast.Expr (len = 2) {
       197  .  .  .  .  .  .  .  .  0: *(obj @ 189)
       198  .  .  .  .  .  .  .  .  1: *ast.Ident {
       199  .  .  .  .  .  .  .  .  .  NamePos: example/stmt.go:14:9
       200  .  .  .  .  .  .  .  .  .  Name: "s"
       201  .  .  .  .  .  .  .  .  .  Obj: *ast.Object {
       202  .  .  .  .  .  .  .  .  .  .  Kind: var
       203  .  .  .  .  .  .  .  .  .  .  Name: "s"
       204  .  .  .  .  .  .  .  .  .  .  Decl: *(obj @ 195)
       205  .  .  .  .  .  .  .  .  .  }
       206  .  .  .  .  .  .  .  .  }
       207  .  .  .  .  .  .  .  }
       208  .  .  .  .  .  .  .  TokPos: example/stmt.go:14:11
       209  .  .  .  .  .  .  .  Tok: :=
       210  .  .  .  .  .  .  .  Rhs: []ast.Expr (len = 1) {
       211  .  .  .  .  .  .  .  .  0: *ast.UnaryExpr {
       212  .  .  .  .  .  .  .  .  .  OpPos: example/stmt.go:14:14
       213  .  .  .  .  .  .  .  .  .  Op: range
       214  .  .  .  .  .  .  .  .  .  X: *ast.Ident {
       215  .  .  .  .  .  .  .  .  .  .  NamePos: example/stmt.go:14:20
       216  .  .  .  .  .  .  .  .  .  .  Name: "strArray"
       217  .  .  .  .  .  .  .  .  .  .  Obj: *(obj @ 131)
       218  .  .  .  .  .  .  .  .  .  }
       219  .  .  .  .  .  .  .  .  }
       220  .  .  .  .  .  .  .  }
       221  .  .  .  .  .  .  }
       222  .  .  .  .  .  }
       223  .  .  .  .  }
       224  .  .  .  .  Value: *(obj @ 198)
  • Tok : like :=
  • X : for i, v := range arr{} -> ast.Ident -> UnaryExpr arr
  • Body : inner {}

IfStmt

if, if-else statement. if-else if statement calls IfStmt recrusive.

type IfStmt struct {
        If   token.Pos // position of "if" keyword
        Init Stmt      // initialization statement; or nil
        Cond Expr      // condition
        Body *BlockStmt
        Else Stmt // else branch; or nil
}
  • If : position
  • Init : if a := true; a {} AssignStmt a := true;
  • Cond
    • if a > 3 {} -> BinaryExpr a > 3
    • if a := true; a {} -> Ident a
  • Body : inner {}
  • Else
    • if a {} else {...} -> BlockStmt {...}
    • if a {} else if {...} -> IfStmt if {...}

SwitchStmt

switch ...{} statement.

type SwitchStmt struct {
    Switch token.Pos  // position of "switch" keyword
    Init   Stmt       // initialization statement; or nil
    Tag    Expr       // tag expression; or nil
    Body   *BlockStmt // CaseClauses only
}
  • Switch : position
  • Init : switch i := 1; i{} AssignStmt i := 1
  • Tag :
    • switch i {} -> Ident i
    • switch {} -> nil
  • Body : inner {}
    • case 1,2,3: -> CaseClause when default, List is nil.
     type CaseClause struct {
            Case  token.Pos // position of "case" or "default" keyword
            List  []Expr    // list of expressions or types; nil means default case
            Colon token.Pos // position of ":"
            Body  []Stmt    // statement list; or nil
        }
- List : it's able to have multi variables like `1,2,3` 

TypeSwitchStmt

type switch statement as follow.

switch v := value.(type) {
    case nil:
    case int:
    default:
}
type TypeSwitchStmt struct {
        Switch token.Pos  // position of "switch" keyword
        Init   Stmt       // initialization statement; or nil
        Assign Stmt       // x := y.(type) or y.(type)
        Body   *BlockStmt // CaseClauses only
}
  • Switch : position
  • Init : same as SwitchStmt
  • Assign : v := value.(type) -> AssignStmt -> Ident := TypeAssertExpr
  • Body : inner {}. same as SwitchStmt

LabeledStmt

label statement like jump: and succeeding one block(statement). not case 1:.

type LabeledStmt struct {
        Label *Ident
        Colon token.Pos // position of ":"
        Stmt  Stmt
}
  • Label :
    44  .  .  .  1: *ast.LabeledStmt {
    45  .  .  .  .  Label: *ast.Ident {
    46  .  .  .  .  .  NamePos: example/label-stmt.go:7:1
    47  .  .  .  .  .  Name: "FOR_LABEL"
    48  .  .  .  .  .  Obj: *ast.Object {
    49  .  .  .  .  .  .  Kind: label
    50  .  .  .  .  .  .  Name: "FOR_LABEL"
    51  .  .  .  .  .  .  Decl: *(obj @ 44)
    52  .  .  .  .  .  }
    53  .  .  .  .  }
  • Colon : position
  • Stmt : succeeding one statement. for example as follow, Stmt -> fmt.Println("hello")
   FOR_LABEL:
        fmt.Println("hello")
        fmt.Println("world")

BranchStmt

break, continue, goto, or fallthrough statement.

type BranchStmt struct {
        TokPos token.Pos   // position of Tok
        Tok    token.Token // keyword token (BREAK, CONTINUE, GOTO, FALLTHROUGH)
        Label  *Ident      // label name; or nil
}
  • TokPos : position
  • Tok
    • break [LABEL_NAME]
    • continue [LABEL_NAME]
    • goto LABEL_NAME
    • fallthrough : goto next case X: block in current switch statement
  • Label
    • break : label-assigned statement have to be ForStmt, SwitchStmt or SelectStmt
    • continue : label-assigned statement have to be ForStmt
    • fallthrough : not semantics but syntax error occured when fallthrough LABEL_NAME...why...
    • if label is nil, I guess most inner ForStmt (or Switch, Select when break) is automatically assigned.

DeferStmt

defer statement

type DeferStmt struct {
        Defer token.Pos // position of "defer" keyword
        Call  *CallExpr
}
  • Defer : position
  • Call : FunctionCall

GoStmt

go functionName() statement.

type GoStmt struct {
        Go   token.Pos // position of "go" keyword
        Call *CallExpr
}
  • Go : position
  • Call : FunctionCall

SelectStmt

channel select statement.

   select {
    case ch <- v:
    default:
    }
type SelectStmt struct {
        Select token.Pos  // position of "select" keyword
        Body   *BlockStmt // CommClauses only
}
  • Select : position
  • Body : CommClauses type CommClause struct { Case token.Pos // position of "case" or "default" keyword Comm Stmt // send or receive statement; nil means default case Colon token.Pos // position of ":" Body []Stmt // statement list; or nil }
    • Case : position
    • Comm :
      • case ch <- v: -> SendStmt
      • case a := <- ch: -> AssignStmt -> Ident := UnaryExpr
      • case 2: -> ExprStmt. I dont know whether semantcally true.
      • cannot use more than 1 statements like case ch <- v, ch <- u
    • Colon : position
    • Body : between current and next case line.

SendStmt

channel sending and receiving like ch <- a.

type SendStmt struct {
        Chan  Expr
        Arrow token.Pos // position of "<-"
        Value Expr
}
  • Chan : ch <- a -> Ident ch
  • Arrow : position
  • Value : ch <- a -> Ident a

EmptyStmt

nop! To use for removing implicit ;?

type EmptyStmt struct {
        Semicolon token.Pos // position of following ";"
        Implicit  bool      // if set, ";" was omitted in the source; added in Go 1.5
}

Type

Type structs are used by FuncDecl, \*Spec.

FuncType

type FuncType struct {
        Func    token.Pos  // position of "func" keyword (token.NoPos if there is no "func")
        Params  *FieldList // (incoming) parameters; non-nil
        Results *FieldList // (outgoing) results; or nil
}

StructType

type StructType struct {
        Struct     token.Pos  // position of "struct" keyword
        Fields     *FieldList // list of field declarations
        Incomplete bool       // true if (source) fields are missing in the Fields list
}
  • Fields : defined fields

InterfaceType

type InterfaceType struct {
        Interface  token.Pos  // position of "interface" keyword
        Methods    *FieldList // list of methods
        Incomplete bool       // true if (source) methods are missing in the Methods list
}
  • Methods : defined methods

ArrayType

type ArrayType struct {
        Lbrack token.Pos // position of "["
        Len    Expr      // Ellipsis node for [...]T array types, nil for slice types
        Elt    Expr      // element type
}
  • Len
    • []int -> nil
    • [3]int -> BasicLit "3"
  • Elt
    • []int -> Ident int

MapType

type MapType struct {
        Map   token.Pos // position of "map" keyword
        Key   Expr
        Value Expr
}
  • Key : map[string]int -> Ident string
  • Value : map[string]int -> Ident int

ChanType

type ChanType struct {
        Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first)
        Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-"); added in Go 1.1
        Dir   ChanDir   // channel direction
        Value Expr      // value type
}
  • Dir : I found Dir : 3, I dont know why.
   const (
            SEND ChanDir = 1 << iota
            RECV
    )
  • Value : c chan int -> Ident int

Literal

BasicLit

Integer, Float, Imaginary(虚数), Char, String literal used by Field struct.

type BasicLit struct {
        ValuePos token.Pos   // literal position
        Kind     token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING
        Value    string      // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o`
}

CompositeLit

Composite literals.

type fuga struct {
    a int
    b string
}

var fooo = fuga{1} // here

var arr = []int{1, 2, 3} // here
type CompositeLit struct {
        Type       Expr      // literal type; or nil
        Lbrace     token.Pos // position of "{"
        Elts       []Expr    // list of composite elements; or nil
        Rbrace     token.Pos // position of "}"
        Incomplete bool      // true if (source) expressions are missing in the Elts list; added in Go 1.11
}
  • Type : type of this struct
  • Elts : var fooo = fuga{1} -> BasicLit 1

FuncLit

Function Literal (non-name func)

var f = func(src string) string { return "<<" + src + ">>" }
type FuncLit struct {
        Type *FuncType  // function type
        Body *BlockStmt // function body
}

Expr

BinaryExpr

1+3, x == 2

type BinaryExpr struct {
    X     Expr        // left operand
    OpPos token.Pos   // position of Op
    Op    token.Token // operator
    Y     Expr        // right operand
}

CallExpr

func(1,2)

type CallExpr struct {
    Fun      Expr      // function expression
    Lparen   token.Pos // position of "("
    Args     []Expr    // function arguments; or nil
    Ellipsis token.Pos // position of "..." (token.NoPos if there is no "...")
    Rparen   token.Pos // position of ")"
}

IndexExpr

arr[2]

type IndexExpr struct {
    X      Expr      // expression
    Lbrack token.Pos // position of "["
    Index  Expr      // index expression
    Rbrack token.Pos // position of "]"
}

KeyValueExpr

var s = MyStruct{key: "value"}

type KeyValueExpr struct {
        Key   Expr
        Colon token.Pos // position of ":"
        Value Expr
}

ParenExpr

(1+2)/3

type ParenExpr struct {
    Lparen token.Pos // position of "("
    X      Expr      // parenthesized expression
    Rparen token.Pos // position of ")"
}

SelectorExpr

fmt.Println("")

type SelectorExpr struct {
        X   Expr   // expression
        Sel *Ident // field selector
}
  • X : fmt.Println("🍣") -> "fmt"
  • Sel : fmt.Println("🍖") -> "Println"

SliceExpr

c := a[2:4]

type SliceExpr struct {
        X      Expr      // expression
        Lbrack token.Pos // position of "["
        Low    Expr      // begin of slice range; or nil
        High   Expr      // end of slice range; or nil
        Max    Expr      // maximum capacity of slice; or nil; added in Go 1.2
        Slice3 bool      // true if 3-index slice (2 colons present); added in Go 1.2
        Rbrack token.Pos // position of "]"
}
  • X : c := a[2:4] -> Ident a
  • Low : c := a[2:4] -> BasicLit 2
  • High : c := a[2:4] -> BasicLit 4
  • Max : c := a[2:4:3] -> BasicLit 3
  • Slice3 : 3-index means a[low : high : max], maybe!

StarExpr

*Hoge

type StarExpr struct {
        Star token.Pos // position of "*"
        X    Expr      // operand
}
  • Star : position
  • X : *Hoge -> Expr of Hoge (ex, CompositeLit)

TypeAssertExpr

3.00.(int)

type TypeAssertExpr struct {
        X      Expr      // expression
        Lparen token.Pos // position of "("; added in Go 1.2
        Type   Expr      // asserted type; nil means type switch X.(type)
        Rparen token.Pos // position of ")"; added in Go 1.2
}
  • X : 3.00.(int) -> BasicLit 3.00(FLOAT)
  • Type :3.00.(int) -> Ident int

UnaryExpr

represents a unary expression exept * (StarExpr).

(ex, !true, var b = &a, ^2)

type UnaryExpr struct {
        OpPos token.Pos   // position of Op
        Op    token.Token // operator
        X     Expr        // operand
}
  • Op : &a -> "&"
  • X : &a -> Ident a

JavaScriptの難読化技術を探る

こんにちは, すいっちとあいぱっどがほしいmonpoke1です.

この記事はTreasure Advent Calendar 2018の6日目の飛び入り記事です.

詳しい人誰か教えてくださいというポエムの意味を込めております.

あらすじ

AdventCalendarの季節.縁あってとあるカレンダーを覗いた.

辞めていった君たちへ · GitHub

最初はAAすごいなぁと思ってたけどよく見たら.jsやん! chromeコンソールに貼り付けて実行してみると面白い.

こういうツールあるのかなぁと思って呟いたらヒントくれた. (知っている方なんだけど, 向こうはmonpoke1が誰だかわかってるのか怪しい.)

ヒントってことはツールじゃなくて自前かぁ...(画像処理は2年前に飽きてるのでやる気を失う)

JavaScript予約語からの文字抽出(([]+1/!1) => "Infinity" とか)

でもこっちちょっと面白そう. 難読化とかいっぱいブログあるんだろうなと思い調べてしまう.

JS難読化手法

seccamp15の資料に基本が載っている.以下はメモ.

前提

  • 難読化は暗号化ではない
    • 多くが"よみにくさ"を目指しているだけ(=時間稼ぎ)
    • すぐにはデコードできないものもある
    • これは確率的に保証されているものではないようだ(ソースを忘れた)

手法の種類(一部)

  • 変数名難読化
    • シンプルに変数名を長くしたり, 1文字にしたり(圧縮の意図が強そう)
  • コードを文字列として隠蔽
    • 文字コードに分解して配列にしたり
    • URLエンコードしたりrot回したり色々
    • 文字列をコードとして実行できる機能が色々あり(DOM-based XSSシンク?), コードを文字列として難読化して実行
      • evals("alert(1)")
      • setTimeout("alert(1)",0)
      • element["innerHTML"] = "<a href=hogehoge/>"
  • プロパティの使用
    • 無駄に文字列, 関数名等を変数(連想配列)に入れておく
    • 以下が等価なことを使用

      1. document.getElementById("link")
      2. document["getElementById"]("link")
      3. x="getElementById"; document[x]("link")
  • jjencode
    • いわゆる"記号プログラミング"ってやつっぽい

記号プログラミンっ

jjencodeとJSF*ck

JavaScript予約語からの文字抽出(([]+1/!1) => "Infinity" とか)

このような手法は, JS記号プログラミングでよく使う手口らしい.

JSF*ckというJavaScriptインタプリタ上で(も)動く記号のみの難読言語があり, これは jjencodeとほぼほぼ同じ原理を踏襲している.

JSFu*kからJS記号プログラミングの知見がちょっとだけ得られた. (検索履歴が汚れるけど, 最近js fu🌟kってよく思うのでちょうどよかった.)

基本的なコード構築

([]+1/!1)[2] => "f" //"Infinity"[2]

のように,

  1. 予約語から一文字取り出しては繋げて実行したいコードを文字列として生成し,
  2. 前述した"文字列をコードとして実行"を行う

が基本的なテンプレらしい. だいたい"constructor"って文字が欲しいがち.

infinityの他にも,

  • (![]+"") => "false"
    • 空配列の反転はfalse
    • 空文字 ""[]を連結すると文字列に(なんでやねん)
  • ({}+[]) => [object Object]
    • 外側の括弧を忘れて{}+[]は文字列ではなく0になった(なんでやねん)
  • [][[]]+[] => "undefined"
    • undefinedはかなり出しやすいので文字数稼ぎになりそう

などがよく使用されるらしい.

見つからない文字どうするんだろう -> 頑張って文字生成一覧を作ってる人がいた.

qiita.com

あとはすっごいがんばってコード長を調節すれば色々できそう.例えば再帰的に...

  1. (![]+"") => "false"
  2. (![[]-{}]+"") => "false"
  3. (![[[]-{}]-{}]+"") => "false"

ツバメマークに見る記号ぷろぐらみんぐ

ここまでもかなり調べにくかったけど, 結局ツバメマーク作れそうな記号と1だけの, (さらに$記号を使わない)JS難読化が全く見つからなかったのでタイムアップ.

コードを眺めて見つけた冗長化をメモ

  • 1|1>>1|1 => 1
    • 演算子の優先順位にドキッとするやつ
    • >> -> | の順に評価される
    • 文字列を長くでき, 見た目の密度が変えられる
  • 11111.1%11.1*111e11|!1 => 7
    • 何が起きてるかよくわからなかったやつ
    • ((11111.1%11.1)*111e11)|!1の順番
    • 7.985612171423826|!1 => 7という知見が得られた(?)
  • 1/[]+[] => "Infinity"
    • そうなんだ...

人間なので上側の羽で飽きました.

おわり

  • JSの謎の仕様は難読化のためにあるのではないかとすら思えてきた
    • 実際にはjjencodeの発明がだいぶ後
  • 難読化周りを調べるにはノイズが多い
    • ひたすらツールの紹介
    • なぜ難読化が必要なの?みたいなさいと
    • 原理紹介全然見つからん
  • いい感じの本も(しら)ない...
    • ラボに刺さってたサイ本(O-REILLY)とかに難読化の項目なかった
  • 有名そうな論文も全然見つからない
    • こちらも"どうparseするか"などばかり
    • この辺で諦め始める
  • この分野 @hasegawayosuke さんしかいないのかという気持ちになった
  • 知見をください!!

参考


明日は ぐりの趣味の話が聞けるっぽい?たのしみですね.ぐんない.

3週間毎朝起きてTreasure2018に参加した話

2018/08/13 ~ 2018/08/31 VOYAGE GROUP Summer Internship「Treasure2018」参加記録

ajito by VOYAGE GROUP

なにこれ

3週間, VOYAGE GROUPの講義+チーム開発なインターンに行った記念ポエム.

意識カルデラ湖な文章.

参加者が参加ブログを公開してわいわいB!すると, またみんなで渋谷でわいわいできるらしい.

そういうことなので, 自分用のメモから加筆削除修正してリリース. 長いかも, すんません.

TL;DR

  • 前半2週間が講義, 後半1週間がチーム開発
  • Go(+React Redux)によるwebアプリケーション
  • フロント, サーバ, HTTP(API), Go, セキュ, DB, アイデアソン等々, めっちゃ頭に詰め込んだ
  • 関わる社員さんの数がすごかった(体感30~40人)
  • インターン生30人で, ワイワイ感が楽しかった

顔とか名前とか覚えるのめちゃめちゃ苦手なんですが, それでも(多分)全員覚えるくらい濃密だった. まとめるとほんとに楽しかった

環境

  • 渋谷駅徒歩(体感)7分
    • 近くにご飯屋がたくさんあるのでQoLが高まった
  • パンゲアと呼ばれるでかい会議室が会場
    • 2人掛けの長机に毎日席替えして座った
    • 何度も椅子から転げ落ちそうになった
  • AJITOという空間も使わせてもらえた
    • 0YENドリンク アイスココアが美味しい
    • 夜は社内バー 0YEN アルコール美味しい
    • 誰かが突然楽器弾き出したりした

来年ビル移転してさらにパワーアップするらしい. 見たい.

講義パート

自分みたいな「web, 何それおいしいの」な人向けの基礎講義かと思ったら普通にちゃんとした講義だった. 大半が手を動かすタイプで, 基本的なgitとかcurl,SQLは慣れてる前提だったかな.

分からないものは社員さんや内定者(バイト)がうろうろして教えてくれた.

タイムテーブル

Time Action
10:00 朝会 指名された人が気合の入る一言を
10:05 講義 社員さんがテーマごとに喋ってくれる,資料もちゃんとしてる
12:00 お昼 近くの席の人と5~8人くらいで食べに行った
13;00 講義 再開
15:00 休憩 おやつタイム! 最高!!
15:30 講義 再開
17:55 夜会 指名された人が締まる一言を
18:00 解散 帰る?ご飯??からのAJITO???

おやつタイムでは, バエるお菓子から出落ちまで, 毎日差し入れが!

dragon-taro(笑)がいい感じにおやつ紹介してたのでリダイレクト.

dragon-taro.com

内容 (PC推奨)

  • 1日目(月) Introducion + Go言語
    • Goでcurl機能実装など, 文法おさらいからhttpまで
    • スズケンさん(@suzu_v)のvim使いが過ぎていた
    • 突然Could not resolve host: localhostして, /etc/hostsを直書きした(なんで?)
  • 2日目(火) Goでサーバ
    • 社員さんらがGoで書いたTodoアプリが配布され, バグの原因を探して直したり機能追加したり
    • webサービスプロダクトほど大きくないが初見には大きく見える, 教材にジャストなサイズ感だった
    • CTOランチ(速答検索クイズ)に当選した, やったー
  • 3日目(水) フロント React + Redux
    • babel,ぷりきゅあ,eslint, webpackなどなども
    • 1番前に座ってても追えないライブコーディング, 絶対に1日で聞く量ではない
    • 難し過ぎたけど, この講義がなければフロントに手がつけられなかった, 絶対必要
  • 4日目(木) API設計
    • 使いやすいAPI設計とは?Restとは? Github, Paypal, AWSAPIを見たり触ったり
    • Zucks(広告配信サービス)のAPI群について, Restfulは要る?認証は?を考えるのがたのしかった
    • やんうぇい先生, Rest Level 5 ってなんですか?
  • 5日目(金) 中間課題
    • TODOアプリを最高に改良 or 自由枠 (HTTPリクエストを飛ばし, Goで書くこと)
    • 後にスズケンさんが全員のコードをガチレビューし, 30分1on1してくれる (60分した)
    • 土日も, 寝たりポケモン映画を見たり(最高だった)中間課題したりした
  • 6日目(月) 中間課題発表会 + セキュリティ
    • 4ブース * 10min/1人 なので, 全員の制作物を見ることは叶わなかった
    • 後半はco3kさん(大半に伝わらないが, zakuroさんにオロナミンCを大量に飲ませた感じの人だった)のセキュリティ攻撃防御
    • co3kさんがなぜか直前で講義を難しくし, template+escapeの嵐で, 阿鼻叫喚した(楽しかった)
    • スライドに登場する絵をLINEスタンプにしてほしい, 買わない気もする
  • 7日目(火) DB設計とマイグレーション
    • 鬼みたいな追加のmigration(downも)を書き,最初にちゃんと設計する大切さを物理で学ぶ
    • あるwebサービスの動作と今後ありうる拡張から, 3人で設計を考え, 揉めに揉めたり(うそ)した
    • 講師の方が3人いて, 頭ミジンコは顔が覚えられず打ち上げで「誰ですか?」してしまった(本当にごめんなさい)
  • 8日目(水) スクラム開発をしよう
    • いいチームとは?スクラム開発とは?講義ののち, 1スプリント12分で実際に街を作った
    • 左隣の班から住みたい街の要件を提示され, 折り紙とか粘土とかであくせく土木工事
    • 最後の方は皆無茶を言っていて面白すぎてお腹が痛かった
      • 「火葬場」を依頼したらオーバーキル可能な火葬場が見事デプロイされた
      • ネットインフラの表現として緩衝材のもじゃもじゃがまかり通っていた

ここで後半のチーム開発のメンバー発表. ドキドキ. 言動やスキルを見て割り振っているらしい.

なんか大学の開発授業でチーム分けの技術的偏りに泣いてる人がいた気がするけど, そういうのは感じなかった.


  • 9日目(木) イデアソン
    • 時流(Youtuberとか)を考え, テーマ(食, 運動, とか)と組み合わせてブレスト
    • User, Needs, Solutionを構築する - 講師Junさん「 UNS1つでも欠けるプロダクトはクofソ」
    • そういえばTreasureのテーマは「Go言語を使って学ぶ、価値のあるもの創りとチーム開発」, ただ技術的にすごい/なんか面白いものではなく, 必要とされるものを作ってナンボなのである
  • 10日目(金) インフラ (+ アイデアを固める)
    • 今回使うcircle ci -> spinnaker -> AWS + k8s を爆速で解説(紹介?)してもらい, 諸々セットアップ
    • いんふらってすげー, ちしきよくメッチャみたせそぉ となった(くらい沢山要素があった)
    • 後半は班でアイデアソン

f:id:monpoke1:20180909031602p:plain
前半のごはんとお菓子と街をじまんするよ

チーム開発パート

わくわくチーム開発(オブラート)になるかと思いきや熱いチーム開発になった.

本当に熱いチーム開発になった. 念のため.

チームとサポーターさん

まずtraPがいたから安心した.

  • 東工大デジタル創作同好会traP
  • 個人でGo+Vue書いてる人
  • icpc アジアいくひと
  • このインターンで初めてプログラミングしたとかいう嘘を言い続けた人
  • プーさん(自分)

の構成だった. これは何もしないを毎日しててもだいじょうぶだプー.

f:id:monpoke1:20180916055913j:plain:w300
だれてます

サポーターとしてエンジニア2人と人事1人がついてくれる. 絶妙なタイミングのサポートで本当に本当に助かった. テディベアと食べログとデリバリーの役割も担ってくださった.

この1週間半完全に業務をせずにつきっきりだった.ということは

全6チーム × 3 ~ 4人 × 1.5週間 = 31週間 = 6人月

ものすごい人件費...(加えて, 他にも社員さんがウロウロしてたので...)

ほかにも, インフラのお兄さん「ここではコード書いて欲しかったので」ということで, インフラで困ったらお兄さんに聞くと魔法で解決した.

めんどくさ, って言いながらやってくれるすごいツンデレリラックマだった.

流れ

はいいか

10.5日目(土日) アイデア固まる, 任意参加で本番環境のチェックなど

11日目(月) 全体: API設計 / サーバ:DB作成, APIモック / フロント: emort作成画面, API送信部分?

12日目(火) サーバ: API実装, セッション周り / フロント: emort作成画面, API送信部分?

13日目(水) サーバ: デバッグ, セキュ / フロント: その他画面作成, css, share画像生成

14日目(木) サーバ: バグ修正, ユーザ登録, テスト / フロント: css, share画像生成デバッグ, その他画面作成 / その他:スライド作成

最終日(金) 発表会 -> 打ち上げ!!

所感

実質3.5日での実装なので, かなりハードだったと思う. それでも, やりたくないでござる と微塵も思わなかった素敵なメンバーと環境だった.

とはいえ1日じゅう開発してるので, 晩御飯食べる頃にはみんな頭がおかしくなっていた. モスバーガーの看板を見るたび「シングルコンシューマキュー厨房で作られるなか, 1つだけ明らかにデプロイが早いモックバーガー」で笑っていたあの頃を思い出して泣いてしまう. 早く帰りたいからモスバーガーにしたのにww

最終日前夜は気づいたらみんな終電がなくなっていた. ぶっちゃけ徹夜しなくても余裕で動くものはできたが, やれるところまでやりたい気がしてやってしまった.夏だし.

f:id:monpoke1:20180909031641p:plain
後半のごはんたち

最終日

イデア面と技術面の評価項目が事前に告知されており, 前半講師陣による評価とインターン生同士による投票で順位がつく.

順位発表や打ち上げでは, アイデアの改善点や将来性などプロの意見をものすごく聞けた. 一方で1.5週間講義を受けた技術面はどの程度評価されたのかまだまだまだまだハンニンマエなのか, あんまりわからなかった. 皆かんがえて苦労して書いたし, そこがすこしさびしかった. (これは, 順位に納得していないとかそういう話では無い. 優勝商品飲めたし. )

打ち上げではもう別れが悲しすぎた.

その他

  • インターン
    • なんかしゃべれるひとおおくてびっくりした, ほんとに情報系か?
    • 席が隣じゃなくても雰囲気でご飯一緒に食べたり雑談したりで, 全然知らない人はいないくらい
    • 数ヶ月後にまた会いたい -> 9/11 Ajitoに10人くらいでお邪魔しました
  • 社内LT
    • Haskellの話とかするって聞いたのに, 気づいたら高度な茶番(AR美少女)を見せられていたり
    • 意味わかんないくらい笑えた, 水樹奈々様とか音響トリビアとかとかとか
  • 社員さん
    • めっちゃいっぱいいた(参考: Slack 86人 うちTreasure生30人)
    • 講義約10日 * 1~3人 + サポーターさん 3~4人 * 6チーム = 30人は少なくともいた
    • カッコいい大人がいっぱいいた
    • daidaiという辛い店が会社全体の行きつけ:辛かった, また行きたい
  • めんせつ
    • 7/10に受けていた
    • 魔法のスプレッドシートからぽちったが, サポー-ターズ系?からこの存在を知る人が多いらしい
    • 関東圏なのに何も考えずにリモート枠予約したり, 挙句時間間違えたりしたので初日顔をあわせるのが怖かった

人生で初めてのインターン. インターンのイメージがTreasureになったので, ほかのインターンが少々物寂しく感じたり感じなかったり.

個人的には, ちゃんとしたweb書いたことなかったし, 今までJSから逃げ続けてきたし, 勉強になったどころの騒ぎではない.

お世話になったすべての人へ, ありがとうございました.

最高でした.

gopher

go gophers by Renee French CC BY 3.0

まさかそんな...gopherくん...


他の参加者のブログを見つけ次第貼っていきます.

polyomino.hatenablog.jp

dragon-taro.com

koukyo1213.hatenablog.com

fcimsb55yn23.hatenablog.com

yoshikawataiki.net

hatsunem.hatenablog.com

shimohiroaki.hatenablog.com

guri-blog.hatenablog.com

↓会社ブログでも色々紹介されてます.

techlog.voyagegroup.com


うーーん, ブログじゃあの時の臨場感や雰囲気を... いわんや感情を伝えきれない...! ということで(雑),

インターン後半のチーム開発で作成したプロダクトに約1ヶ月半テコ入れをし, リリースする運びとなりました!

その名も emort です.

emort.life

アッOGP見切れてるっ...!

ブログより気軽に, ツイッターよりまとめやすく, 出来事や感情をまとめてシェアできるサービス(のハズ)です!! ぜひ一度触っていただけたら嬉しいです.

ちなみにこんな感じの記事ができます.

そしてご意見ご感想お待ちしております✨ -> @monpoke1