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
- token.IMPORT
- 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
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.Identfunc(...){...}
-> ast.FuncTypestruct{...}
-> ast.StructTypetype 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 }
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
-> Identint
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の時点で全ての情報を展開しているようだ. その後Value
やX
では, 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
でなければならない.
- break : ラベルが付加されているstatementは
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 ofHoge
(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
- token.IMPORT
- 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.Identfunc(...){...}
: ast.FuncTypestruct{...}
: ast.StructTypetype 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[] -> BasicLit1
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++ {}
: AssignStmti := 0
- Cond
for i := 0; i < 10; i++ {}
: BinaryExpri < 10
for coundown > 0 {}
: BinaryExprcoundown > 0
- Post
for i := 0; i < 10; i++ {}
: IncDecStmti++
- 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 -> UnaryExprarr
- 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 {}
AssignStmta := true;
- Cond
if a > 3 {}
-> BinaryExpra > 3
if a := true; a {}
-> Identa
- Body : inner
{}
- Else
if a {} else {...}
-> BlockStmt{...}
if a {} else if {...}
-> IfStmtif {...}
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{}
AssignStmti := 1
- Tag :
switch i {}
-> Identi
switch {}
-> nil
- Body : inner
{}
case 1,2,3:
-> CaseClause whendefault
, 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 asSwitchStmt
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 nextcase X:
block in currentswitch
statement
- Label
- break : label-assigned statement have to be
ForStmt
,SwitchStmt
orSelectStmt
- 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.
- break : label-assigned statement have to be
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:
-> SendStmtcase a := <- ch:
-> AssignStmt -> Ident := UnaryExprcase 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
-> Identch
- Arrow : position
- Value :
ch <- a
-> Identa
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 }
MapType
type MapType struct { Map token.Pos // position of "map" keyword Key Expr Value Expr }
- Key :
map[string]int
-> Identstring
- Value :
map[string]int
-> Identint
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
-> Identint
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}
-> BasicLit1
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]
-> Identa
- Low :
c := a[2:4]
-> BasicLit2
- High :
c := a[2:4]
-> BasicLit4
- Max :
c := a[2:4:3]
-> BasicLit3
- 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 ofHoge
(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)
-> BasicLit3.00(FLOAT)
- Type :
3.00.(int)
-> Identint
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
-> Identa
JavaScriptの難読化技術を探る
こんにちは, すいっちとあいぱっどがほしいmonpoke1です.
この記事はTreasure Advent Calendar 2018の6日目の飛び入り記事です.
詳しい人誰か教えてくださいというポエムの意味を込めております.
あらすじ
AdventCalendarの季節.縁あってとあるカレンダーを覗いた.
最初はAAすごいなぁと思ってたけどよく見たら.jsやん! chromeコンソールに貼り付けて実行してみると面白い.
こういうツールあるのかなぁと思って呟いたらヒントくれた. (知っている方なんだけど, 向こうはmonpoke1が誰だかわかってるのか怪しい.)
お、ではヒントを!
— R.Yokoji (@yokoji) 2018年12月2日
・JavaScriptの予約語からの文字抽出(([]+1/!1) => "Infinity" とか)
・画像の2値化・ビットマップ化・密度計算
・からの文字列長&画像サイズ調整したら、文字をビットマップ割当
・半角文字の縦横比は要考慮
・改行、空白でJavaScriptとして解釈不能にならないように
ヒントってことはツールじゃなくて自前かぁ...(画像処理は2年前に飽きてるのでやる気を失う)
JavaScriptの予約語からの文字抽出(([]+1/!1) => "Infinity" とか)
でもこっちちょっと面白そう. 難読化とかいっぱいブログあるんだろうなと思い調べてしまう.
JS難読化手法
seccamp15の資料に基本が載っている.以下はメモ.
前提
- 難読化は暗号化ではない
- 多くが"よみにくさ"を目指しているだけ(=時間稼ぎ)
- すぐにはデコードできないものもある
- これは確率的に保証されているものではないようだ(ソースを忘れた)
手法の種類(一部)
- 変数名難読化
- シンプルに変数名を長くしたり, 1文字にしたり(圧縮の意図が強そう)
- コードを文字列として隠蔽
- プロパティの使用
- 無駄に文字列, 関数名等を変数(連想配列)に入れておく
以下が等価なことを使用
-
document.getElementById("link")
-
document["getElementById"]("link")
-
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]
のように,
- 予約語から一文字取り出しては繋げて実行したいコードを文字列として生成し,
- 前述した"文字列をコードとして実行"を行う
が基本的なテンプレらしい. だいたい"constructor"って文字が欲しいがち.
infinityの他にも,
(![]+"") => "false"
- 空配列の反転はfalse
- 空文字
""
や[]
を連結すると文字列に(なんでやねん)
({}+[]) => [object Object]
- 外側の括弧を忘れて
{}+[]
は文字列ではなく0になった(なんでやねん)
- 外側の括弧を忘れて
[][[]]+[] => "undefined"
- undefinedはかなり出しやすいので文字数稼ぎになりそう
などがよく使用されるらしい.
見つからない文字どうするんだろう -> 頑張って文字生成一覧を作ってる人がいた.
あとはすっごいがんばってコード長を調節すれば色々できそう.例えば再帰的に...
(![]+"") => "false"
(![[]-{}]+"") => "false"
(![[[]-{}]-{}]+"") => "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 さんしかいないのかという気持ちになった
- 知見をください!!
参考
- JavaScript難読化読経
- 記号だけのJavaScriptプログラミングの基本原理
- (」・ω・)」うー!(/・ω・)/にゃー!encode
- JSFuck - GitHub
- 演算子の優先順位
- JSFuckから理解するECMAScriptの仕様
明日は ぐりの趣味の話が聞けるっぽい?たのしみですね.ぐんない.
3週間毎朝起きてTreasure2018に参加した話
2018/08/13 ~ 2018/08/31 VOYAGE GROUP Summer Internship「Treasure2018」参加記録
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(笑)がいい感じにおやつ紹介してたのでリダイレクト.
内容 (PC推奨)
- 1日目(月) Introducion + Go言語
- 2日目(火) Goでサーバ
- 社員さんらがGoで書いたTodoアプリが配布され, バグの原因を探して直したり機能追加したり
- webサービスプロダクトほど大きくないが初見には大きく見える, 教材にジャストなサイズ感だった
- CTOランチ(速答検索クイズ)に当選した, やったー
- 3日目(水) フロント React + Redux
- babel,ぷりきゅあ,eslint, webpackなどなども
- 1番前に座ってても追えないライブコーディング, 絶対に1日で聞く量ではない
- 難し過ぎたけど, この講義がなければフロントに手がつけられなかった, 絶対必要
- 4日目(木) API設計
- 5日目(金) 中間課題
- 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日目(金) インフラ (+ アイデアを固める)
チーム開発パート
わくわくチーム開発(オブラート)になるかと思いきや熱いチーム開発になった.
本当に熱いチーム開発になった. 念のため.
チームとサポーターさん
まずtraPがいたから安心した.
の構成だった. これは何もしないを毎日しててもだいじょうぶだプー.
サポーターとしてエンジニア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
最終日前夜は気づいたらみんな終電がなくなっていた. ぶっちゃけ徹夜しなくても余裕で動くものはできたが, やれるところまでやりたい気がしてやってしまった.夏だし.
最終日
アイデア面と技術面の評価項目が事前に告知されており, 前半講師陣による評価とインターン生同士による投票で順位がつく.
順位発表や打ち上げでは, アイデアの改善点や将来性などプロの意見をものすごく聞けた. 一方で1.5週間講義を受けた技術面はどの程度評価されたのかまだまだまだまだハンニンマエなのか, あんまりわからなかった. 皆かんがえて苦労して書いたし, そこがすこしさびしかった. (これは, 順位に納得していないとかそういう話では無い. 優勝商品飲めたし. )
打ち上げではもう別れが悲しすぎた.
その他
- インターン生
- なんかしゃべれるひとおおくてびっくりした, ほんとに情報系か?
- 席が隣じゃなくても雰囲気でご飯一緒に食べたり雑談したりで, 全然知らない人はいないくらい
- 数ヶ月後にまた会いたい -> 9/11 Ajitoに10人くらいでお邪魔しました
- 社内LT
- 社員さん
- めっちゃいっぱいいた(参考: Slack 86人 うちTreasure生30人)
- 講義約10日 * 1~3人 + サポーターさん 3~4人 * 6チーム = 30人は少なくともいた
- カッコいい大人がいっぱいいた
- daidaiという辛い店が会社全体の行きつけ:辛かった, また行きたい
- めんせつ
- 7/10に受けていた
- 魔法のスプレッドシートからぽちったが, サポー-ターズ系?からこの存在を知る人が多いらしい
- 関東圏なのに何も考えずにリモート枠予約したり, 挙句時間間違えたりしたので初日顔をあわせるのが怖かった
人生で初めてのインターン. インターンのイメージがTreasureになったので, ほかのインターンが少々物寂しく感じたり感じなかったり.
個人的には, ちゃんとしたweb書いたことなかったし, 今までJSから逃げ続けてきたし, 勉強になったどころの騒ぎではない.
お世話になったすべての人へ, ありがとうございました.
最高でした.
すずけんさんにサインもらった pic.twitter.com/5oifmIChJe
— もんぽけ (@monpoke1) 2018年8月31日
まさかそんな...gopherくん...
他の参加者のブログを見つけ次第貼っていきます.
↓会社ブログでも色々紹介されてます.
うーーん, ブログじゃあの時の臨場感や雰囲気を... いわんや感情を伝えきれない...! ということで(雑),
インターン後半のチーム開発で作成したプロダクトに約1ヶ月半テコ入れをし, リリースする運びとなりました!
その名も emort です.
アッOGP見切れてるっ...!
ブログより気軽に, ツイッターよりまとめやすく, 出来事や感情をまとめてシェアできるサービス(のハズ)です!! ぜひ一度触っていただけたら嬉しいです.
ちなみにこんな感じの記事ができます.
そしてご意見ご感想お待ちしております✨ -> @monpoke1