├── .travis.yml ├── LICENSE ├── README.md ├── f.swift ├── swift-dump.rb ├── test.sh └── test.swift /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | script: ./test.sh 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Boris Bügling 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swift Dump 2 | 3 | [![Build Status](http://img.shields.io/travis/neonichu/swift-dump.svg?style=flat)](https://travis-ci.org/neonichu/swift-dump) 4 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 5 | 6 | PoC of a class-dumpy tool for Swift classes. 7 | 8 | ## Usage 9 | 10 | ``` 11 | $ file test 12 | test: Mach-O 64-bit executable x86_64 13 | $ ./swift-dump.rb test 14 | // Code generated from `test` 15 | import Foundation 16 | 17 | class Foo { 18 | let i: Int = 0 19 | var j: Int = 0 20 | var k: Int { return 0 } 21 | func add(Int, b : Int) -> String { return "" } 22 | func bar() -> Int { return 0 } 23 | func nothing() -> () {} 24 | } 25 | ``` 26 | 27 | ## Status 28 | 29 | This is __very__ limited right now, with no support for structs, generics or initializers. 30 | 31 | ## Author 32 | 33 | Boris Bügling, boris@icculus.org 34 | 35 | ## Help needed 36 | 37 | Follow [@NeoNacho](https://twitter.com/NeoNacho) to help me beat [@orta](https://twitter.com/orta) in followers count. 38 | 39 | ## License 40 | 41 | swift-dump is available under the MIT license. See the LICENSE file for more info. 42 | -------------------------------------------------------------------------------- /f.swift: -------------------------------------------------------------------------------- 1 | class MyClass { 2 | var someVar = 1 3 | 4 | func someFuncWithAReallyLongNameLol() { 5 | } 6 | } 7 | 8 | -------------------------------------------------------------------------------- /swift-dump.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | module Swift 4 | def self.demangle_symbol(symbol) 5 | `xcrun swift-demangle #{symbol}`.split('---> ')[1][0..-2] 6 | end 7 | 8 | def self.default_value(type) 9 | case type 10 | when 'Int' 11 | return '0' 12 | when String 13 | return '""' 14 | end 15 | 16 | 'nil' 17 | end 18 | 19 | class Executable 20 | def initialize(executable) 21 | nm_out = `xcrun nm -g #{executable}` 22 | 23 | @executable = executable 24 | @symbols = nm_out.lines.map { |line| line.split(' ')[2] }.compact 25 | end 26 | 27 | def swift_symbols 28 | @symbols.select { |sym| sym[/^__T/] } 29 | end 30 | 31 | def to_s 32 | dump = <<-DUMP 33 | // Code generated from `#{@executable}` 34 | import Foundation 35 | 36 | DUMP 37 | 38 | classes.each do |sclass| 39 | c_name = class_name(sclass) 40 | out_file = File.new("#{c_name.split('.')[1]}.swift", "w") 41 | 42 | dump << "class #{c_name.split('.')[1]} {\n" 43 | out_file.puts("class #{c_name.split('.')[1]} {\n") 44 | 45 | variables(sclass).each do |var| 46 | v = Swift::demangle_symbol(var).split('.')[2] 47 | 48 | mangled_type = var.gsub(/^#{sclass}g[0-9]+#{v}/, '') 49 | type = Swift::demangle_symbol(var).split(' : ')[1] 50 | type = type.gsub(/Swift\./, '') 51 | 52 | init = '' 53 | if has_direct_field(sclass, v, mangled_type) 54 | init = " = #{Swift::default_value(type)} " 55 | end 56 | 57 | declaration = 'var' 58 | unless has_setter(sclass, v, mangled_type) 59 | if has_direct_field(sclass, v, mangled_type) 60 | declaration = 'let' 61 | else 62 | init = " { return #{Swift::default_value(type)} } " 63 | end 64 | end 65 | 66 | dump << "#{declaration} #{v}: #{type}#{init}\n" 67 | out_file.puts("#{declaration} #{v}: #{type}#{init}\n") 68 | end 69 | 70 | functions(sclass).each do |func| 71 | f = Swift::demangle_symbol(func).gsub(/ \(#{c_name}\)/, '') 72 | f = f.gsub(/^#{c_name}\./, '') 73 | f = f.gsub(/Swift\./, '') 74 | 75 | return_statement = '' 76 | return_type = f.split('-> ')[1] 77 | if return_type != '()' 78 | return_statement = " return #{Swift::default_value(return_type)} " 79 | end 80 | 81 | dump << "func #{f} {#{return_statement}}\n" 82 | out_file.puts("func #{f} {#{return_statement}}\n") 83 | end 84 | 85 | dump << "}\n" 86 | out_file.puts("}\n") 87 | out_file.close 88 | end 89 | 90 | dump 91 | end 92 | 93 | private 94 | 95 | CLASS = '__TFC' 96 | DEALLOC = 'D' 97 | DIRECT = '__TWvdvC' 98 | 99 | def classes 100 | relevant_symbols.select { |sym| sym.end_with?(DEALLOC) }.map { |sym| sym[0..-2] } 101 | end 102 | 103 | def class_name(mangled_class_prefix) 104 | Swift::demangle_symbol(mangled_class_prefix + DEALLOC).split('.__')[0] 105 | end 106 | 107 | def has_direct_field(mangled_class_prefix, variable_name, mangled_type) 108 | prefix = mangled_class_prefix.gsub(CLASS, DIRECT) 109 | expected_sym = "#{prefix}#{variable_name.length}#{variable_name}#{mangled_type}" 110 | @symbols.select{ |sym| sym == expected_sym }.length > 0 111 | end 112 | 113 | def functions(mangled_class_prefix) 114 | relevant_symbols.select { |sym| sym[/^#{mangled_class_prefix}[0-9]+/] } 115 | end 116 | 117 | def has_materialize(mangled_class_prefix, variable_name, mangled_type) 118 | expected_sym = "#{mangled_class_prefix}m[0-9]+#{variable_name}#{mangled_type}" 119 | relevant_symbols.select { |sym| sym[/^#{expected_sym}/] }.length > 0 120 | end 121 | 122 | def has_setter(mangled_class_prefix, variable_name, mangled_type) 123 | expected_sym = "#{mangled_class_prefix}s[0-9]+#{variable_name}#{mangled_type}" 124 | relevant_symbols.select { |sym| sym[/^#{expected_sym}/] }.length > 0 125 | end 126 | 127 | def relevant_symbols 128 | @symbols.select { |sym| sym[/^#{CLASS}/] } 129 | end 130 | 131 | def variables(mangled_class_prefix) 132 | relevant_symbols.select { |sym| sym[/^#{mangled_class_prefix}g[0-9]+/] } 133 | end 134 | end 135 | end 136 | 137 | ############################################################################## 138 | 139 | if ARGV.first.nil? 140 | puts 'Please provide the path to an executable as first argument' 141 | exit 2 142 | end 143 | 144 | exec = Swift::Executable.new(ARGV.first) 145 | puts exec 146 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | symbols() { 4 | xcrun nm -g "$1"|awk '{print $3}'|grep '^__T'|sort|uniq 5 | } 6 | 7 | run_test() { 8 | xcrun -sdk macosx swiftc "$1.swift" 9 | ./swift-dump.rb "$1"|xcrun -sdk macosx swiftc - 10 | 11 | symbols "$1" >"$1.syms" 12 | symbols main|sed "s/4main/${#1}$1/g" >main.syms 13 | diff "$1.syms" main.syms 14 | } 15 | 16 | trap 'rm -f f test main *.syms' EXIT 17 | set -e 18 | 19 | run_test "test" 20 | run_test "f" 21 | -------------------------------------------------------------------------------- /test.swift: -------------------------------------------------------------------------------- 1 | class Foo { 2 | let i: Int = 0 3 | var j = 1 4 | var k: Int { return 2 } 5 | 6 | func bar() -> Int { 7 | return 23 8 | } 9 | 10 | func add(a: Int, b: Int) -> String { 11 | return "" 12 | } 13 | 14 | func nothing() { 15 | } 16 | } 17 | 18 | --------------------------------------------------------------------------------