- Product Manager
- Web Engineer
- Customer Support
- Other occupations (60)
- Development
-
Business
- Product Manager
- プロダクトマネージャー
- 広報
- カルチャー推進・浸透
- 知財戦略立案・推進・発明発掘
- リスクマネジメント統括本部
- 内部監査
- AML/CFTコンプライアンス
- AML・金融犯罪対策Ops
- 金融コンプライアンス
- システム監査
- ビジネス採用担当
- 経営企画(予実・IR)
- HRBP
- Legal
- 債権管理/MFK
- ToB Sales
- インサイドセールス
- フィールドセールス
- インサイドセールス SDR
- インサイドセールス企画
- オンラインセールス
- SaaS営業、MFBC
- インサイドセールス MFBC
- セールス MFBC
- マーケティングリサーチャー
- マーケター
- データマーケター
- BtoBマーケティングリーダー
- CRMスペシャリスト
- イベントマーケター
- Other
スマフォ開発チームの高地です。
iOS開発界隈ではSwiftに注目が集まっています。
iOS8からはObjective-Cから解放されることで、喜んでいる開発者も多いのではないでしょうか。
ということで、今回はSwiftのREPL機能についてお話しします。
Swiftの2つのREPLモード
SwiftにはREPLが用意されているので、 Ruby、Python、Node.js等のLL言語のように簡単に動作確認ができます。
ところがSwift –helpをみていたところ、実はREPLモードが2つあることにきがつきました。
-integrated-repl Integrated REPL mode-repl REPL mode
たぶん、-replは普通一般的なREPLモードだと思われます。それでは-integrated-replモードとはどんなことができるのでしょうか?
調査環境はXcode6-Beta2です。
SwiftのREPL:-integrated-replモード
まずは-integrated-replモードに切り替え、対話モードに入ります。実際にこのモードのオプションはどのようなものがあるのか:helpでたたいてみました。
:quit - quit the interpreter (you can also use :exit or Control+D or exit(0))
:autoindent (on|off) - turn on/off automatic indentation of bracketed lines
:constraints debug (on|off) - turn on/off the debug output for the constraint-based type checker
:dump_ir - dump the LLVM IR generated by the REPL
:dump_ast - dump the AST representation of the REPL input
:dump_decl - dump the AST representation of the named declarations
:dump_source - dump the user input (ignoring lines with errors)
:print_decl - print the AST representation of the named declarations
:print_module - print the decls in the given module, but not submodules
-integrated-replモードには9つのオプションが用意されています。
:quite、:autoindentは想像がつくので省略し、1つずつ見てみましょう。
constraints debug
:constraints debug (on|off) – turn on/off the debug output for the constraint-based type checker
XcodeのConstraintsのためのデバッグオプションの設定のようです。
ここでon, offをきりかえて、一般的なREPL modeでデバッグを行うのでしょうか・・・デバッグ環境を用意するのも手間なので、これ以上は時間をかけないでおきます。
dump_ir
:dump_ir – dump the LLVM IR generated by the REPL
LLVMのIR(Intermediate Representation)ということで、LLVMでコンパイルされた中間コードを出力するようです、実際にREPLに入力してみます。MoenyForwardという文字列をdataというmutableな変数に代入するだけという1行コードです。
(swift) var data:String = "MoneyForward"
// data : String = "MoneyForward"
たったこれだけではありあますが:dump_irで出力させてみると
swift) :dump_ir
; ModuleID = 'REPL'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-darwin13.2.0"
%0 = type
%1 = type
%2 = type
%3 = type
%4 = type
%5 = type { i8**, %6 }
%6 = type { i64 }
%7 = type { void (%8*)*, i8**, %6 }
%8 = type { %6*, i32, i32 }
%9 = type { i8*, i8*, i8*, i8*, i8*, i8*, i8*, i8*, i32, i32 }
%10 = type
%11 = type { [24 x i8], %6*, i8** }
%12 = type { i8*, %8* }
%13 = type
%14 = type { [24 x i8], %6*, i8** }
%15 = type
%16 = type
%17 = type
%18 = type
%19 = type { [24 x i8], %6*, i8** }
%20 = type { [24 x i8], %6*, i8** }
%21 = type { [24 x i8], %6*, i8** }
%22 = type
%23 = type
%24 = type
%25 = type { [24 x i8], %6* }
%26 = type
%27 = type
%28 = type
%29 = type { %objc_object* }
%30 = type { %objc_object* }
%31 = type
%32 = type
%33 = type
%34 = type
%35 = type
%36 = type
%swift.type_pattern = type opaque
%objc_object = type opaque
%swift.opaque = type opaque
%CSs17HeapBufferStorage = type opaque
@_Tv4REPL4dataSS = global %0 zeroinitializer, align 8
@0 = private unnamed_addr constant [13 x i16] [i16 77, i16 111, i16 110, i16 101, i16 121, i16 70, i16 111, i16 114, i16 119, i16 97, i16 114, i16 100, i16 0]
@1 = private unnamed_addr constant [20 x i16] [i16 47, i1
(省略)・・・
; :13 ; preds = %4
%14 = bitcast %29* %1 to %34*
%._storage = getelementptr inbounds %34* %14, i32 0, i32 0
%._storage.storage = getelementptr inbounds %35* %._storage, i32 0, i32 0
%15 = bitcast %36* %._storage.storage to i64*
%16 = load i64* %15, align 8
%17 = inttoptr i64 %16 to %8*
call void @swift_retain_noresult(%8* %17) #2
%18 = getelementptr inbounds %29* %1, i32 0, i32 0
%19 = load %objc_object** %18, align 8
call void @objc_release(%objc_object* %19) #2
call void @objc_release(%objc_object* %0) #2
ret i64 %16
}
attributes #0 = { nounwind readnone }
attributes #1 = { noreturn nounwind }
attributes #2 = { nounwind }
!llvm.module.flags = !{!0, !1, !2, !5, !6, !7, !8}
!0 = metadata !{i32 2, metadata !"Dwarf Version", i32 3}
!1 = metadata !{i32 1, metadata !"Debug Info Version", i32 1}
!2 = metadata !{i32 6, metadata !"Linker Options", metadata !3}
!3 = metadata !{metadata !4}
!4 = metadata !{metadata !"-lswift_stdlib_core"}
!5 = metadata !{i32 1, metadata !"Objective-C Version", i32 2}
!6 = metadata !{i32 1, metadata !"Objective-C Image Info Version", i32 0}
!7 = metadata !{i32 1, metadata !"Objective-C Image Info Section", metadata !"__DATA, __objc_imageinfo, regular, no_dead_strip"}
!8 = metadata !{i32 4, metadata !"Objective-C Garbage Collection", i32 0}
(swift)
かなりアセンブリチックな中間コードが約6000行ほど表示されました。
グローバルIDやローカルID(%swift.opaque)などが大量に出力されているのがわかります。
まさしく中間コードが出力されました。たった1行の宣言コードでしたが、コンパイルするための最適化のためでしょうか、非常に大量のコードが生成されているようです。
dump_ast
:dump_ast– dump the AST representation of the REPL input
これも上の:dumo_irと似ている印象を受けますが、コンパイル過程のAST(抽象構文木)が出力されます。
swift) :dump_ast
(source_file
(top_level_code_decl
(brace_stmt
(tuple_expr type='()' location=:1:5 range=[:1:5 - line:1:6]))
(top_level_code_decl
(brace_stmt
(pattern_binding_decl
(pattern_typed type='String'
(pattern_named type='String' 'data')
(type_ident
(component id='String' bind=type)))
(call_expr implicit type='String' location=:1:19 range=[:1:19 - line:1:19]
(dot_syntax_call_expr type='(RawPointer, numberOfCodeUnits: Word) -> String' location=:1:19 range=[:1:19 - line:1:19]
(declref_expr implicit type='String.Type -> (RawPointer, numberOfCodeUnits: Word) -> String' location=:1:19 range=[:1:19 - line:1:19] decl=Swift.(file).String._convertFromBuiltinUTF16StringLiteral specialized=no)
(type_expr implicit type='String.Type' location=:1:19 range=[:1:19 - line:1:19] typerepr='<>'))
(string_literal_expr type='(Builtin.RawPointer, numberOfCodeUnits: Builtin.Word)' location=:1:19 range=[:1:19 - line:1:19] encoding=utf16 value="MoneyForward")))
)
(top_level_code_decl
(brace_stmt
(call_expr implicit type='()' location=:1:1 range=[:1:1 - line:1:1]
(closure_expr type='(String) -> ()' location=:1:1 range=[:1:1 - line:1:1] discriminator=0
(brace_stmt
(call_expr implicit type='()' location=:1:1 range=[:1:1 - line:1:1]
(declref_expr implicit type='(String) -> ()' location=:1:1 range=[:1:1 - line:1:1] decl=Swift.(file).print [with T=String] specialized=no)
(call_expr implicit type='String' location=:1:1 range=[:1:1 - line:1:1]
(dot_syntax_call_expr type='(RawPointer, numberOfCodeUnits: Word) -> String' location=:1:1 range=[:1:1 - line:1:1]
(declref_expr implicit type='String.Type -> (RawPointer, numberOfCodeUnits: Word) -> String' location=:1:1 range=[:1:1 - line:1:1] decl=Swift.(file).String._convertFromBuiltinUTF16StringLiteral specialized=no)
(type_expr implicit type='String.Type' location=:1:1 range=[:1:1 - line:1:1] typerepr='<>'))
(string_literal_expr type='(Builtin.RawPointer, numberOfCodeUnits: Builtin.Word)' location=:1:1 range=[:1:1 - line:1:1] encoding=utf16 value="// data : String = ")))
(call_expr implicit type='()' location=:1:1 range=[:1:1 - line:1:1]
(declref_expr implicit type='(String) -> ()' location=:1:1 range=[:1:1 - line:1:1] decl=Swift.(file).debugPrintln [with T=String] specialized=no)
(declref_expr implicit type='String' location=:1:1 range=[:1:1 - line:1:1] decl=REPL.(file).top-level code.explicit closure discriminator=0.arg@:1:1 specialized=no))))
(paren_expr type='(String)' location=:1:1 range=[:1:1 - line:1:1]
(load_expr implicit type='String' location=:1:1 range=[:1:1 - line:1:1]
(declref_expr implicit type='@lvalue String' location=:1:1 range=[:1:1 - line:1:1] decl=REPL.(file).data@:1:5 specialized=no)))))
(var_decl "data" type='String' storage_kind='stored'))
まだ、ASTのほうが、コード解析段階なので、人間の目にやさしいです。行数も36行程度なので、そこまで苦労せずにリーディングできそうです。
(declref_expr implicit type='String.Type -> (RawPointer, numberOfCodeUnits: Word) -> String' location=:1:1 range=[:1:1 - line:1:1] decl=Swift.(file).String._convertFromBuiltinUTF16StringLiteral specialized=no)
このあたりみると、内部的にUTF-16に変換していることが推測できます。
dump_decl
:dump_decl – dump the AST representation of the named declaration
は、AST内で定義されている変数をnameに指定することで、内容が確認できます。上記で書いたdata変数を指定してみると
(swift) :dump_decl data
(var_decl "data" type='String' storage_kind='stored')
とAST内での解析されたdata変数の内容が出てきます。var_declが変数定義を表し、typeが型、storage_kindはなんでしょう?変数が参照渡しなのか、値渡しを表しているのでしょうか、、、ちょっと不明ですね。
dump_source
:dump_source – dump the user input (ignoring lines with errors)
これは、ユーザーが入力したソースコードが出力されます。そのままですね。
(swift) :dump_source
var data:String = "MoneyForward"
そのまま表示されました。
print_decl
:print_decl – print the AST representation of the named declarations
は、:dump_declオプション時よりももっと簡略的に出力されるようです
(swift) :print_decl data
var data: String
こちらはほぼ、素のSwiftコード定義に近い出力結果となりました。
:print_module – print the decls in the given module, but not submodule
は、Swiftで作成されたモジュールの内容を出力してくれるようです。実際にモジュールを作成してみればよいのですが、うまく作成できませんでした。何かよいモジュールはないか、色々とためしたところ、Swift自身を指定できるみたいです。
print_module swift
(swift) :print_module swift
を行うとSwift内部で定義されている、構造体、Extensions、プロトコル、など基底となる構造がずらずらとでてきます。何個かピックアップしますと、
struct UnsafePointer : BidirectionalIndex, Comparable, Hashable, LogicValue {
var value: RawPointer
init()
init(_ value: RawPointer)
init(_ other: COpaquePointer)
init(_ value: Int)
init(_ from: UnsafePointer)
static func null() -> UnsafePointer
static func alloc(num: Int) -> UnsafePointer
func dealloc(num: Int)
var memory: T {
@transparent get {}
@transparent set {}
}
func initialize(newvalue: T)
func move() -> T
func moveInitializeBackwardFrom(source: UnsafePointer, count: Int)
func moveAssignFrom(source: UnsafePointer, count: Int)
func moveInitializeFrom(source: UnsafePointer, count: Int)
func initializeFrom(source: UnsafePointer, count: Int)
func initializeFrom(source: C)
func destroy()
func destroy(count: Int)
var _isNull: Bool {
@transparent get {}
}
@transparent func getLogicValue() -> Bool
subscript (i: Int) -> T {
@transparent get {}
@transparent set {}
}
var hashValue: Int {
get {}
}
func succ() -> UnsafePointer
func pred() -> UnsafePointer
@conversion @transparent func __conversion() -> CMutablePointer
func __conversion() -> CMutableVoidPointer
@conversion @transparent func __conversion() -> CConstPointer
@conversion @transparent func __conversion() -> CConstVoidPointer
@conversion @transparent func __conversion() -> AutoreleasingUnsafePointer
init(_ cp: CConstPointer)
init(_ cm: CMutablePointer)
init(_ op: AutoreleasingUnsafePointer)
init(_ cp: CConstVoidPointer)
init(_ cp: CMutableVoidPointer)
}
ポイントをキャストするためのUnsafePointer構造体や
extension Dictionary : Printable, DebugPrintable {
func _makeDescription(#isDebug: Bool) -> String
var description: String {
get {}
}
var debugDescription: String {
get {}
}
}
extension Dictionary : Reflectable {
func getMirror() -> Mirror
}
Dictionaryなどなどおなじみの構造が確認できます。ここから考えるに-integrated-replにおいて、Swift自身もモジュールというあつかいなのかなと。
ちなみに、対話モードにしなくても、出力結果をコンソール上に出力する方法もあります。shellのechoコマンドで、内部で実行したいオプションをわたしてあげても、同じ結果が得られます。
echo :print_module Swift | xcrun swift -integrated-repl
公式の情報が少ないので、本質的な-integrated-replの使い方はまだわかりませんが、このようにコンパイルする過程の状態(AST、IR)を容易に確認できる手段がREPLで用意されているのというのは驚きでした。Swiftを使う開発者が、より最適なコーディングを理解するための情報を得るツールとしても使えますね。
このような開発ツールの充実度を高めようとしている姿勢からAppleのSwiftにかける本気度を感じました。
マネーフォワードでは、他のエンジニアとは異なった視点で、問題を解決できる、マニアックなエンジニアも募集中です。