├── .circleci └── config.yml ├── .gitignore ├── Cargo.toml ├── Dockerfile_kcov ├── LICENSE ├── README.md ├── examples ├── bigint_fact.cs ├── bigint_fact.exe ├── boxing.cs ├── boxing.exe ├── calc.cs ├── calc.exe ├── char.cs ├── char.exe ├── dll_a.cs ├── dll_a.exe ├── dll_b.cs ├── dll_b.dll ├── dll_c.cs ├── dll_c.dll ├── dll_d.cs ├── dll_d.dll ├── fibo.cs ├── fibo.exe ├── float.cs ├── float.exe ├── game_of_life.cs ├── game_of_life.exe ├── hello.cs ├── hello.exe ├── prime.cs ├── prime.exe ├── smallpt.cs ├── smallpt.exe ├── smallpt.sh ├── virtual.cs └── virtual.exe ├── run_kcov.sh └── src ├── exec ├── decode.rs ├── instruction.rs ├── jit │ ├── builtin.rs │ ├── cfg.rs │ ├── jit.rs │ └── mod.rs └── mod.rs ├── lib.rs ├── macros.rs ├── main.rs ├── metadata ├── assembly.rs ├── class.rs ├── header.rs ├── image.rs ├── metadata.rs ├── method.rs ├── mod.rs ├── pe_parser.rs ├── signature.rs └── token.rs └── util ├── mod.rs ├── name_path.rs └── resolver.rs /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | build: 5 | docker: 6 | - image: ubuntu 7 | 8 | working_directory: /opt/yacht 9 | 10 | steps: 11 | - checkout 12 | 13 | - setup_remote_docker: 14 | docker_layer_caching: true 15 | 16 | - run: 17 | name: Install Docker client 18 | command: | 19 | apt-get update 20 | apt-get install -y curl 21 | set -x 22 | VER="17.03.0-ce" 23 | curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz 24 | tar -xz -C /tmp -f /tmp/docker-$VER.tgz 25 | mv /tmp/docker/* /usr/bin 26 | 27 | - run: | 28 | docker build -t yacht -f Dockerfile_kcov . 29 | docker run --security-opt seccomp=unconfined yacht bash run_kcov.sh $CODECOV_TOKEN 30 | 31 | # - run: 32 | # command: set -eux 33 | # - run: 34 | # command: apt-get update 35 | # - run: 36 | # command: apt-get install -y libgc-dev zlib1g-dev wget libssl-dev pkg-config cmake zlib1g-dev curl binutils-dev libcurl4-openssl-dev libdw-dev libiberty-dev git make build-essential 37 | # - run: 38 | # command: | 39 | # wget "https://static.rust-lang.org/rustup/dist/x86_64-unknown-linux-gnu/rustup-init" 40 | # chmod +x rustup-init 41 | # ./rustup-init -y --no-modify-path --default-toolchain nightly 42 | # RUSTUP_HOME=~/.cargo/bin/rustup 43 | # CARGO_HOME=~/.cargo/bin/cargo 44 | # chmod -R a+w $RUSTUP_HOME $CARGO_HOME; 45 | # rm rustup-init 46 | # source ~/.cargo/env 47 | # - run: 48 | # name: Install llvm-6.0 and so on 49 | # command: | 50 | # apt-get install clang-6.0 llvm-6.0 llvm-6.0-dev opt libedit-dev build-essential -y 51 | # # ln -s /usr/bin/clang-6.0 /usr/bin/clang; 52 | # # ln -s /usr/bin/clang++-6.0 /usr/bin/clang++; 53 | # ln -s /usr/bin/llvm-config-6.0 /usr/bin/llvm-config; 54 | # - run: 55 | # name: Setting up kcov 56 | # command: | 57 | # git clone https://github.com/SimonKagstrom/kcov 58 | # cd kcov 59 | # git checkout 9db5fa58986c2eae39e82580f15ba6fadb2dc906 60 | # cmake . 61 | # make -j 62 | # make install 63 | # - run: 64 | # name: Test 65 | # command: | 66 | # export PATH=~/.cargo/bin:$PATH 67 | # cargo test --no-run 68 | # 69 | # # REPORT=$(find ./target/debug -maxdepth 1 -name 'yacht-*' -a ! -name '*.d') 70 | # # for file in $REPORT; do 71 | # # echo $file 72 | # # /usr/local/bin/kcov --include-pattern=yacht/src --exclude-pattern=/.cargo ./target/cov "$file" 73 | # # done 74 | # # bash <(curl -s https://codecov.io/bash) -s ./target/cov 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yacht" 3 | version = "0.1.0" 4 | authors = ["uint256_t"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | clap = "2.32.0" 9 | ansi_term = "0.9.0" 10 | rustc-hash = "1.0.1" 11 | llvm-sys = "60.0.0" 12 | id-arena = "2.2.1" 13 | 14 | [profile.dev] 15 | codegen-units = 16 16 | 17 | [profile.release] 18 | codegen-units = 16 19 | opt-level = 3 20 | debug = false 21 | lto = true 22 | debug-assertions = false 23 | panic = "unwind" 24 | -------------------------------------------------------------------------------- /Dockerfile_kcov: -------------------------------------------------------------------------------- 1 | FROM rustlang/rust:nightly 2 | 3 | RUN set -x && \ 4 | apt-get update && \ 5 | apt-get upgrade -y && \ 6 | apt-get install zlib1g-dev apt-utils -y && \ 7 | apt-get install opt libedit-dev build-essential make libgc-dev -y; \ 8 | apt-get install software-properties-common -y; 9 | 10 | RUN set -x && \ 11 | apt-add-repository -y "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch-6.0 main" && \ 12 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|apt-key add - && \ 13 | apt-get update && \ 14 | apt-get install -y llvm-6.0 && \ 15 | ln -s /usr/bin/llvm-config-6.0 /usr/bin/llvm-config; 16 | 17 | RUN set -x && \ 18 | apt-get install -y cmake g++ pkg-config jq && \ 19 | apt-get install -y libcurl4-openssl-dev libelf-dev libdw-dev binutils-dev libiberty-dev && \ 20 | cargo install cargo-kcov && \ 21 | cargo kcov --print-install-kcov-sh | sh 22 | 23 | ADD . /opt/rapidus 24 | 25 | WORKDIR /opt/rapidus 26 | 27 | RUN set -x && \ 28 | export PATH=~/.cargo/bin:$PATH && \ 29 | export PATH=~/usr/local/bin:$PATH && \ 30 | rustup override set nightly 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 uint256_t 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yacht 2 | 3 | [![CircleCI](https://circleci.com/gh/maekawatoshiki/yacht.svg?style=shield)](https://circleci.com/gh/maekawatoshiki/yacht) 4 | [![codecov](https://codecov.io/gh/maekawatoshiki/yacht/branch/master/graph/badge.svg)](https://codecov.io/gh/maekawatoshiki/yacht/branch/master) 5 | [![](http://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) 6 | 7 | ECMA-335 (Common Language Infrastructure, .NET) implementation written in Rust. 8 | 9 | *Just for fun* 10 | 11 | # Building from Source 12 | 13 | ## Building on Linux-like systems 14 | 15 | 1. Install Rust 16 | 17 | Run the command below and follow the onscreen instructions. 18 | 19 | ```sh 20 | curl https://sh.rustup.rs -sSf | sh 21 | ``` 22 | 23 | 2. Use Rust Nightly 24 | 25 | ```sh 26 | rustup override set nightly 27 | ``` 28 | 29 | 3. Install dependencies 30 | - LLVM 6.0 31 | - Boehm GC 32 | - (Other packages as necessary...) 33 | 34 | ```sh 35 | # e.g. Ubuntu or Debian 36 | apt-get install llvm-6.0 libgc-dev 37 | ``` 38 | 39 | 4. Test 40 | 41 | ```sh 42 | cargo test 43 | ``` 44 | 45 | 5. Run 46 | 47 | ```sh 48 | cargo run --release examples/XXX.exe 49 | ``` 50 | 51 | -------------------------------------------------------------------------------- /examples/bigint_fact.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | class BigIntList { 4 | public int n; 5 | public BigIntList next; 6 | 7 | public BigIntList(int n_) { 8 | n = n_; 9 | next = null; 10 | } 11 | 12 | public override string ToString() { 13 | return (next != null ? next.ToString() : "" ) + n + " "; 14 | } 15 | } 16 | 17 | class BigInt { 18 | public BigIntList list; 19 | 20 | public BigInt(int n) { 21 | list = new BigIntList(n); 22 | } 23 | 24 | public BigInt Add(BigInt val) { 25 | int carry = 0; 26 | BigInt c = new BigInt(0); 27 | BigIntList al = list; 28 | BigIntList bl = val.list; 29 | BigIntList cl = c.list; 30 | 31 | for (;;) { 32 | cl.n += carry; 33 | 34 | if (al != null) { cl.n += al.n; al = al.next; } 35 | if (bl != null) { cl.n += bl.n; bl = bl.next; } 36 | 37 | carry = cl.n / 1000000000; 38 | cl.n %= 1000000000; 39 | 40 | if (al != null || bl != null || carry > 0) { 41 | cl.next = new BigIntList(0); 42 | cl = cl.next; 43 | } else break; 44 | } 45 | 46 | return c; 47 | } 48 | 49 | public BigInt Mul(BigInt val, int n) { 50 | var ret = new BigInt(0); 51 | for (int i = 0; i < n; i++) ret = ret.Add(val); 52 | return ret; 53 | } 54 | 55 | public override string ToString() { 56 | return list.ToString(); 57 | } 58 | } 59 | 60 | public class A { 61 | public static void Main() { 62 | var n = new BigInt(1); 63 | for (int i = 0; i < 100; i++) { 64 | n = n.Mul(n, i + 1); 65 | Console.WriteLine((i + 1) + "! = " + n); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/bigint_fact.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/bigint_fact.exe -------------------------------------------------------------------------------- /examples/boxing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | public class A { 3 | public static void Main() { 4 | Console.WriteLine("num: {0}", 2); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /examples/boxing.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/boxing.exe -------------------------------------------------------------------------------- /examples/calc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class Node { 4 | public virtual void Dump() {} 5 | public virtual int Eval() { return 0; } 6 | } 7 | 8 | class OpNode : Node { 9 | public char op; 10 | public Node lhs; 11 | public Node rhs; 12 | 13 | public OpNode(char op_, Node lhs_, Node rhs_) { 14 | op = op_; 15 | lhs = lhs_; 16 | rhs = rhs_; 17 | } 18 | 19 | public override void Dump() { 20 | Console.Write(" ("); 21 | Console.Write(op); 22 | lhs.Dump(); 23 | rhs.Dump(); 24 | Console.Write(")"); 25 | } 26 | 27 | public override int Eval() { 28 | int l = lhs.Eval(), r = rhs.Eval(); 29 | if (op == '+') return l + r; 30 | if (op == '*') return l * r; 31 | return 0; 32 | } 33 | } 34 | 35 | public class NumNode : Node { 36 | public char num; 37 | 38 | public NumNode(char num_) { 39 | num = num_; 40 | } 41 | 42 | public override void Dump() { 43 | Console.Write(" "); 44 | Console.Write(num); 45 | } 46 | 47 | public override int Eval() { return num - '0'; } 48 | } 49 | 50 | public class Parser { 51 | public string expr; 52 | public int pos, len; 53 | 54 | public Parser(string expr_) { 55 | expr = expr_; 56 | pos = 0; 57 | len = expr.Length; 58 | } 59 | 60 | public Node Parse() { 61 | Console.Write("Expression: "); 62 | Console.WriteLine(expr); 63 | return ExprAdd(); 64 | } 65 | 66 | Node ExprAdd() { 67 | Node left = ExprMul(); 68 | while (!End() && GetChar() == '+') { 69 | char op = NextChar(); 70 | Node right = ExprMul(); 71 | left = new OpNode(op, left, right); 72 | } 73 | return left; 74 | } 75 | 76 | Node ExprMul() { 77 | Node left = ExprDigit(); 78 | while (!End() && GetChar() == '*') { 79 | char op = NextChar(); 80 | Node right = ExprDigit(); 81 | left = new OpNode(op, left, right); 82 | } 83 | return left; 84 | } 85 | 86 | Node ExprDigit() { 87 | return new NumNode(NextChar()); 88 | } 89 | 90 | bool End() { return pos >= len; } 91 | char NextChar() { return expr[pos++]; } 92 | char GetChar() { return expr[pos]; } 93 | } 94 | 95 | public class Calc { 96 | public static void Main() { 97 | var parser = new Parser("1+2*3+4"); 98 | var node = parser.Parse(); 99 | 100 | Console.Write("S expr:"); 101 | node.Dump(); 102 | 103 | Console.Write("\nEval: "); 104 | Console.WriteLine(node.Eval()); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /examples/calc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/calc.exe -------------------------------------------------------------------------------- /examples/char.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class GetChar { 4 | public static void Main() { 5 | var s = "hello あいうえお"; 6 | Console.WriteLine(s[0]); 7 | Console.WriteLine(s[6]); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/char.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/char.exe -------------------------------------------------------------------------------- /examples/dll_a.cs: -------------------------------------------------------------------------------- 1 | public class A { 2 | public static void Main() { 3 | B.hi(); 4 | var b = new B(1, 2); 5 | b.show(); 6 | (new D("another dll")).show(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/dll_a.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/dll_a.exe -------------------------------------------------------------------------------- /examples/dll_b.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | public class B { 3 | int x, y; 4 | public B(int x_, int y_) { 5 | x = x_; y = y_; 6 | } 7 | public void show() { 8 | Console.WriteLine("[class B] " + (x + y)); 9 | } 10 | public static void hi() { 11 | C.hi(); 12 | Console.WriteLine("[class B] Hi"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/dll_b.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/dll_b.dll -------------------------------------------------------------------------------- /examples/dll_c.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | public class C { 3 | B b; 4 | public static void hi() { 5 | Console.WriteLine("[class C] Hi"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/dll_c.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/dll_c.dll -------------------------------------------------------------------------------- /examples/dll_d.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | public class D { 3 | string msg; 4 | public D(string msg_) { 5 | msg = msg_; 6 | } 7 | public void show() { 8 | Console.WriteLine("[class D] " + msg); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/dll_d.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/dll_d.dll -------------------------------------------------------------------------------- /examples/fibo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class Hello { 4 | static int fibo(int x) { 5 | if (x < 2) return 1; 6 | return fibo(x - 1) + fibo(x - 2); 7 | } 8 | 9 | public static void Main() { 10 | Console.WriteLine(fibo(35)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/fibo.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/fibo.exe -------------------------------------------------------------------------------- /examples/float.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class Floating { 4 | public static void Main() { 5 | double x = 1.2, y = 3.4; 6 | Console.WriteLine(x + y); 7 | Console.WriteLine(x - y); 8 | Console.WriteLine(x * y); 9 | Console.WriteLine(x / y); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/float.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/float.exe -------------------------------------------------------------------------------- /examples/game_of_life.cs: -------------------------------------------------------------------------------- 1 | // Recommend not to run with Interpreter. It's too slow. 2 | 3 | using System; 4 | 5 | public class GameOfLife { 6 | static void DrawGrid(bool[] grid, int grid_width, int grid_height) { 7 | string s = ""; 8 | for (int y = 1; y < grid_height; y++) { 9 | for (int x = 1; x < grid_width; x++) { 10 | s += grid[x + grid_width * y] ? "#" : " "; 11 | } 12 | s += "\n"; 13 | } 14 | Console.WriteLine(s); 15 | } 16 | 17 | static void UpdateGrid(bool[] grid, bool[] copy_grid, int grid_width, int grid_height) { 18 | for (int y = 1; y < grid_height - 1; y++) { 19 | for (int x = 1; x < grid_width - 1; x++) { 20 | int total_cells = 0; 21 | 22 | total_cells += grid[(x - 1) + grid_width * (y - 1)] ? 1 : 0; 23 | total_cells += grid[(x - 1) + grid_width * (y )] ? 1 : 0; 24 | total_cells += grid[(x - 1) + grid_width * (y + 1)] ? 1 : 0; 25 | total_cells += grid[(x ) + grid_width * (y - 1)] ? 1 : 0; 26 | total_cells += grid[(x ) + grid_width * (y + 1)] ? 1 : 0; 27 | total_cells += grid[(x + 1) + grid_width * (y - 1)] ? 1 : 0; 28 | total_cells += grid[(x + 1) + grid_width * (y )] ? 1 : 0; 29 | total_cells += grid[(x + 1) + grid_width * (y + 1)] ? 1 : 0; 30 | 31 | if (grid[x + grid_width * y]) { 32 | if (total_cells == 0 || total_cells == 1) { 33 | copy_grid[x + y * grid_width] = false; 34 | } else if (total_cells == 2 || total_cells == 3) { 35 | copy_grid[x + y * grid_width] = true; 36 | } else if (4 <= total_cells && total_cells <= 8) { 37 | copy_grid[x + y * grid_width] = false; 38 | } else { 39 | copy_grid[x + y * grid_width] = false; 40 | } 41 | } else { 42 | if (total_cells == 3) { 43 | copy_grid[x + y * grid_width] = true; 44 | } else { 45 | copy_grid[x + y * grid_width] = false; 46 | } 47 | } 48 | } 49 | } 50 | 51 | for (int i = 0; i < grid.Length; i++) { 52 | grid[i] = copy_grid[i]; 53 | } 54 | } 55 | 56 | static void FillWithGlider(bool[] grid, int grid_width) { 57 | int x = 0, y = 1; 58 | string s = 59 | $@"--------------------------------------------- 60 | --------------------X------------------------ 61 | --------------------X-X---------------------- 62 | ---------------------X-X-------X------------- 63 | --------XX-----------X--X------XX------------ 64 | --------XX-----------X-X--XX----XX----------- 65 | --------------------X-X---XX----XXX-------XX- 66 | --------------------X-----XX----XX--------XX- 67 | -------------------------------XX------------ 68 | -------------------------------X------------- 69 | ---------------------------------------------"; 70 | for (var i = 0; i < s.Length; i++) { 71 | var c = s[i]; 72 | if (c == '\n') { y++; x = 0; continue; } 73 | if (c == 'X') grid[x + grid_width * y] = true; 74 | x++; 75 | } 76 | } 77 | 78 | public static void Main() { 79 | int grid_width = 53, grid_height = 43; 80 | bool[] grid = new bool[grid_width * grid_height]; 81 | bool[] copy_grid = new bool[grid_width * grid_height]; 82 | 83 | FillWithGlider(grid, grid_width); 84 | 85 | for (int i = 0; i < 100; i++) { 86 | DrawGrid(grid, grid_width, grid_height); 87 | UpdateGrid(grid, copy_grid, grid_width, grid_height); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /examples/game_of_life.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/game_of_life.exe -------------------------------------------------------------------------------- /examples/hello.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class Hello { 4 | public static void Main() { 5 | Console.WriteLine("Hello World"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/hello.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/hello.exe -------------------------------------------------------------------------------- /examples/prime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class Random { 4 | private uint x; 5 | private uint y; 6 | private uint z; 7 | private uint w; 8 | 9 | public Random(uint seed) { 10 | SetSeed(seed); 11 | } 12 | 13 | public void SetSeed(uint seed) { 14 | x = 521288629; 15 | y = 341235113; 16 | z = seed; 17 | w = x ^ z; 18 | } 19 | 20 | public uint Next() { 21 | uint t = x ^ (x << 11); 22 | x = y; 23 | y = z; 24 | z = w; 25 | w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); 26 | return w; 27 | } 28 | 29 | public uint Next(int max) { 30 | return Next() % (uint)max; 31 | } 32 | } 33 | 34 | public class Prime { 35 | public static bool MillerRabinTest(int n, Random r) { 36 | if ((n < 2) || (n % 2 == 0)) return n == 2; 37 | 38 | int s = n - 1; 39 | while (s % 2 == 0) s >>= 1; 40 | 41 | for (int i = 0; i < 20; i++) { 42 | int a = (int)r.Next(n - 1) + 1; 43 | int temp = s; 44 | long mod = 1; 45 | for (int j = 0; j < temp; ++j) mod = (mod * a) % n; 46 | while (temp != n - 1 && mod != 1 && mod != n - 1) { 47 | mod = (mod * mod) % n; 48 | temp *= 2; 49 | } 50 | if (mod != n - 1 && temp % 2 == 0) return false; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | static bool IsPrime(int n) { 57 | if (n == 2) return true; 58 | if (n % 2 == 0 || n <= 1) return false; 59 | for (int k = 3; k * k <= n; k += 2) 60 | if (n % k == 0) 61 | return false; 62 | return true; 63 | } 64 | 65 | static void EratosthenesSieve(int max) { 66 | bool[] a = new bool[max]; 67 | a[0] = false; 68 | for (int i = 1; i < max; i++) 69 | a[i] = true; 70 | for (int i = 0; i * i < max; i++) 71 | if (a[i]) 72 | for (int k = i + 1; (i + 1) * k <= max; k++) 73 | a[(i + 1) * k - 1] = false; 74 | for (int i = 0; i < max; i++) 75 | if (a[i]) Console.WriteLine(i + 1); 76 | } 77 | 78 | public static void Main() { 79 | Console.WriteLine("Normal"); 80 | for (int i = 2; i < 100; i++) { 81 | if (IsPrime(i)) Console.WriteLine(i); 82 | } 83 | 84 | Console.WriteLine("Eratosthenes Sieve"); 85 | EratosthenesSieve(100); 86 | 87 | Console.WriteLine("Miller Rabin"); 88 | Random r = new Random(13245); 89 | for (int i = 100; i < 300; i++) { 90 | if (MillerRabinTest(i, r)) Console.WriteLine(i); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /examples/prime.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/prime.exe -------------------------------------------------------------------------------- /examples/smallpt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | public class Random { 4 | private uint x; 5 | private uint y; 6 | private uint z; 7 | private uint w; 8 | 9 | public Random(uint seed) { 10 | SetSeed(seed); 11 | } 12 | 13 | public void SetSeed(uint seed) { 14 | x = 521288629; 15 | y = 341235113; 16 | z = seed; 17 | w = x ^ z; 18 | } 19 | 20 | public uint Next() { 21 | uint t = x ^ (x << 11); 22 | x = y; 23 | y = z; 24 | z = w; 25 | w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); 26 | return w; 27 | } 28 | 29 | public double NextDouble() { 30 | return (double)Next() / 0xfffffff0; 31 | } 32 | } 33 | 34 | public class Vec { 35 | public double x, y, z; // position, also color (r,g,b) 36 | public Vec(double x_, double y_, double z_) { x = x_; y = y_; z = z_; } 37 | public static Vec operator +(Vec a, Vec b) { return new Vec(a.x + b.x, a.y + b.y, a.z + b.z); } 38 | public static Vec operator -(Vec a, Vec b) { return new Vec(a.x - b.x, a.y - b.y, a.z - b.z); } 39 | public static Vec operator *(Vec a, double b) { return new Vec(a.x * b, a.y * b, a.z * b); } 40 | public Vec mult(Vec b) { return new Vec(x * b.x, y * b.y, z * b.z); } 41 | public Vec norm() { return this * (1 / Math.Sqrt(x * x + y * y + z * z)); } 42 | public double dot(Vec b) { return x * b.x + y * b.y + z * b.z; } // cross: 43 | public static Vec operator %(Vec a, Vec b) { return new Vec(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); } 44 | } 45 | 46 | public enum Refl { DIFF, SPEC, REFR }; 47 | 48 | public class Ray { 49 | public Sphere s; 50 | public double t; 51 | public Vec o, d; 52 | public Ray(Vec o_, Vec d_) { o = o_; d = d_; } 53 | public bool intersect(Sphere[] spheres) { 54 | double inf = t = 1e20, d; 55 | for (int i = spheres.Length - 1; i >= 0; i--) { d = spheres[i].intersect(this); if (d > 0 && d < t) { t = d; s = spheres[i]; } } 56 | return t < inf; 57 | } 58 | } 59 | 60 | public class Sphere { 61 | public double rad; // radius 62 | public Vec p, e, c; // position, emission, color 63 | public Refl refl; // reflection type (DIFFuse, SPECular, REFRactive) 64 | public Sphere(double rad_, Vec p_, Vec e_, Vec c_, Refl refl_) { 65 | rad = rad_; p = p_; e = e_; c = c_; refl = refl_; 66 | } 67 | public double intersect(Ray r) { // returns distance, 0 if nohit 68 | Vec op = p - r.o; // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0 69 | double t, eps = 1e-4, b = op.dot(r.d), det = b * b - op.dot(op) + rad * rad; 70 | if (det < 0) return 0; else det = Math.Sqrt(det); 71 | return (t = b - det) > eps ? t : ((t = b + det) > eps ? t : 0); 72 | } 73 | } 74 | 75 | internal static class Program { 76 | static double clamp(double x) { return x < 0 ? 0 : x > 1 ? 1 : x; } 77 | static int toInt(double x) { return (int)(Math.Pow(clamp(x), 1 / 2.2) * 255 + .5); } 78 | static Vec radiance(Sphere[] spheres, Ray r, int depth, Random random) { 79 | if (!r.intersect(spheres)) return new Vec(0, 0, 0); // if miss, return black 80 | Sphere obj = r.s; // the hit object 81 | Vec x = r.o + r.d * r.t, n = (x - obj.p).norm(), nl = n.dot(r.d) < 0 ? n : n * -1, f = obj.c; 82 | double p = f.x > f.y && f.x > f.z ? f.x : f.y > f.z ? f.y : f.z; // max refl 83 | if (depth > 100) { 84 | return obj.e; // *** Added to prevent stack overflow 85 | } 86 | if (++depth > 5) if (random.NextDouble() < p) f = f * (1 / p); else return obj.e; //R.R. 87 | if (obj.refl == Refl.DIFF) { // Ideal DIFFUSE reflection 88 | double r1 = 2 * Math.PI * random.NextDouble(), r2 = random.NextDouble(), r2s = Math.Sqrt(r2); 89 | Vec w = nl, u = ((Math.Abs(w.x) > .1 ? new Vec(0, 1, 0) : new Vec(1, 0, 0)) % w).norm(), v = w % u; 90 | Vec d = (u * Math.Cos(r1) * r2s + v * Math.Sin(r1) * r2s + w * Math.Sqrt(1 - r2)).norm(); 91 | return obj.e + f.mult(radiance(spheres, new Ray(x, d), depth, random)); 92 | } 93 | else if (obj.refl == Refl.SPEC) // Ideal SPECULAR reflection 94 | return obj.e + f.mult(radiance(spheres, new Ray(x, r.d - n * 2 * n.dot(r.d)), depth, random)); 95 | Ray reflRay = new Ray(x, r.d - n * 2 * n.dot(r.d));// Ideal dielectric REFRACTION 96 | bool into = n.dot(nl) > 0; // Ray from outside going in? 97 | double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.dot(nl), cos2t; 98 | if ((cos2t = 1 - nnt * nnt * (1 - ddn * ddn)) < 0) // Total internal reflection 99 | return obj.e + f.mult(radiance(spheres, reflRay, depth, random)); 100 | Vec tdir = (r.d * nnt - n * ((into ? 1 : -1) * (ddn * nnt + Math.Sqrt(cos2t)))).norm(); 101 | double a = nt - nc, b = nt + nc, R0 = a * a / (b * b), c = 1 - (into ? -ddn : tdir.dot(n)); 102 | double Re = R0 + (1 - R0) * c * c * c * c * c, Tr = 1 - Re, P = .25 + .5 * Re, RP = Re / P, TP = Tr / (1 - P); 103 | return obj.e + f.mult(depth > 2 ? (random.NextDouble() < P ? // Russian roulette 104 | radiance(spheres, reflRay, depth, random) * RP 105 | : radiance(spheres, new Ray(x, tdir), depth, random) * TP) 106 | : radiance(spheres, reflRay, depth, random) * Re + radiance(spheres, new Ray(x, tdir), depth, random) * Tr); 107 | } 108 | private static void Main() { 109 | int w = 100, h = 100, samps = 40; 110 | Ray cam = new Ray(new Vec(50, 52, 295.6), new Vec(0, -0.042612, -1).norm()); // cam pos, dir 111 | Vec cx = new Vec(w * .5135 / h, 0, 0), cy = (cx % cam.d).norm() * .5135; 112 | var c = new Vec[w*h]; 113 | var random = new Random(12345); 114 | 115 | Sphere[] spheres = new Sphere[] { 116 | new Sphere(1e5, new Vec( 1e5+1,40.8,81.6), new Vec(0,0,0),new Vec(.75,.75,.25),Refl.DIFF), //Left 117 | new Sphere(1e5, new Vec(-1e5+99,40.8,81.6),new Vec(0,0,0),new Vec(.25,.25,.75),Refl.DIFF), //Rght 118 | new Sphere(1e5, new Vec(50,40.8, 1e5), new Vec(0,0,0),new Vec(.75,.75,.75),Refl.DIFF), //Back 119 | new Sphere(1e5, new Vec(50,40.8,-1e5+170), new Vec(0,0,0),new Vec(.75,.75,.75),Refl.SPEC), //Frnt 120 | new Sphere(1e5, new Vec(50, 1e5, 81.6), new Vec(0,0,0),new Vec(.75,.75,.75),Refl.DIFF), //Botm 121 | new Sphere(1e5, new Vec(50,-1e5+81.6,81.6),new Vec(0,0,0),new Vec(.75,.75,.75),Refl.DIFF), //Top 122 | new Sphere(16.5,new Vec(27,16.5,47), new Vec(0,0,0),new Vec(1,1,1)*.999, Refl.SPEC), //Mirr 123 | new Sphere(16.5,new Vec(73,16.5,78), new Vec(0,0,0),new Vec(1,1,1)*.999, Refl.REFR), //Glas 124 | new Sphere(600, new Vec(50,681.6-.27,81.6),new Vec(12,12,12), new Vec(0,0,0), Refl.DIFF), //Lite 125 | }; 126 | 127 | // wada (http://www.kevinbeason.com/smallpt/extraScenes.txt) 128 | // double R=60; 129 | // double T=30*Math.PI/180; 130 | // double D=R/Math.Cos(T); 131 | // double Z=60; 132 | // spheres[0] = new Sphere(1e5, new Vec(50, 100, 0), new Vec(3,3,3), new Vec(0, 0, 0), 0); // sky 133 | // spheres[1] = new Sphere(1e5, new Vec(50, -1e5-D-R, 0), new Vec(0,0,0), new Vec(.1,.1,.1),0); // grnd 134 | // spheres[2] = new Sphere(60, new Vec(50,40.8,62)+new Vec(Math.Cos(T),Math.Sin(T),0)*D, new Vec(0,0,0), (new Vec(1,.3,.3))*.999, 1); //red 135 | // spheres[3] = new Sphere(60, new Vec(50,40.8,62)+new Vec(-Math.Cos(T),Math.Sin(T),0)*D, new Vec(0,0,0), (new Vec(.3,1,.3))*.999, 1); //grn 136 | // spheres[4] = new Sphere(60, new Vec(50,40.8,62)+new Vec(0,-1,0)*D, new Vec(0, 0, 0),(new Vec(.3,.3,1))*.999, 1); //blue 137 | // spheres[5] = new Sphere(60, new Vec(50,40.8,62)+new Vec(0,0,-1)*D, new Vec(0, 0, 0),(new Vec(.53,.53,.53))*.999, 1); //back 138 | // spheres[6] = new Sphere(60, new Vec(50,40.8,62)+new Vec(0,0,1)*D, new Vec(0, 0, 0), (new Vec(1,1,1))*.999, 2); //front 139 | 140 | for (int i = 0; i < (w * h); i++) { 141 | var x = i % w; 142 | var y = h - i / w - 1; 143 | var color = new Vec(0,0,0); 144 | Console.Write("\rRendering " + ((int)100.0*i/(w*h-1)) + "%"); 145 | for (int sy = 0; sy < 2; sy++) { // 2x2 subpixel rows 146 | for (int sx = 0; sx < 2; sx++) { // 2x2 subpixel cols 147 | Vec r = new Vec(0,0,0); 148 | for (int s = 0; s < samps; s++) { 149 | double r1 = 2 * random.NextDouble(), dx = r1 < 1 ? Math.Sqrt(r1) - 1 : 1 - Math.Sqrt(2 - r1); 150 | double r2 = 2 * random.NextDouble(), dy = r2 < 1 ? Math.Sqrt(r2) - 1 : 1 - Math.Sqrt(2 - r2); 151 | Vec d = cx * (((sx + .5 + dx) / 2 + x) / w - .5) + cy * (((sy + .5 + dy) / 2 + y) / h - .5) + cam.d; 152 | d = d.norm(); 153 | r = r + radiance(spheres, new Ray(cam.o + d * 140, d), 0, random) * (1.0 / samps); 154 | } 155 | color = color + new Vec(clamp(r.x), clamp(r.y), clamp(r.z)) * .25; 156 | } 157 | } 158 | c[i] = color; 159 | } 160 | 161 | Console.WriteLine("\nP3 " + w + " " + h + " 255"); 162 | for (int i = 0; i < w * h; i++) { 163 | Console.Write(toInt(c[i].x)); Console.Write(' '); 164 | Console.Write(toInt(c[i].y)); Console.Write(' '); 165 | Console.Write(toInt(c[i].z)); Console.WriteLine(' '); 166 | } 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /examples/smallpt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/smallpt.exe -------------------------------------------------------------------------------- /examples/smallpt.sh: -------------------------------------------------------------------------------- 1 | cargo run --release $(dirname $0)/smallpt.exe | tee image_.ppm 2 | cat image_.ppm | tail -n +2 > image.ppm 3 | rm image_.ppm 4 | -------------------------------------------------------------------------------- /examples/virtual.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | class Shape { 4 | public virtual void Say() { 5 | Console.WriteLine("Shape"); 6 | } 7 | } 8 | 9 | class Triangle : Shape { 10 | public override void Say() { 11 | Console.WriteLine("Triangle"); 12 | } 13 | } 14 | 15 | class Rectangle : Shape { 16 | public override void Say() { 17 | Console.WriteLine("Rectangle"); 18 | } 19 | } 20 | 21 | public class Sample { 22 | public static void Main() { 23 | Shape shape = new Shape(); 24 | shape.Say(); 25 | 26 | shape = new Triangle(); 27 | shape.Say(); 28 | 29 | shape = new Rectangle(); 30 | shape.Say(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/virtual.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maekawatoshiki/yacht/e20fff3c9cc10a1944c74df4a76a519a76d3e8ae/examples/virtual.exe -------------------------------------------------------------------------------- /run_kcov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cargo test 3 | cargo kcov --verbose 4 | bash <(curl -s https://codecov.io/bash) -s ./target/cov -t $1 5 | -------------------------------------------------------------------------------- /src/exec/decode.rs: -------------------------------------------------------------------------------- 1 | use crate::{exec::instruction::*, metadata::token::*}; 2 | use rustc_hash::FxHashMap; 3 | use std::{iter::Enumerate, mem::transmute, slice::Iter}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct BytesToInstructions<'a> { 7 | iter: Enumerate>, 8 | target_map: FxHashMap, 9 | } 10 | 11 | impl<'a> BytesToInstructions<'a> { 12 | pub fn new(bytes: &'a [u8]) -> Self { 13 | Self { 14 | iter: bytes.iter().enumerate(), 15 | target_map: FxHashMap::default(), 16 | } 17 | } 18 | 19 | pub fn convert(&mut self) -> Option> { 20 | let mut iseq = vec![]; 21 | 22 | self.make_target_map(); 23 | 24 | while let Some((i, byte)) = self.iter.next() { 25 | match *byte { 26 | il_instr::LDNULL => iseq.push(Instruction::Ldnull), 27 | il_instr::LDSTR => { 28 | let token = self.read_u32()?; 29 | assert!(token & 0xff00_0000 == 0x7000_0000); 30 | let us_offset = token & 0x00ff_ffff; 31 | iseq.push(Instruction::Ldstr(us_offset)) 32 | } 33 | il_instr::CALL => iseq.push(Instruction::Call(Token(self.read_u32()?))), 34 | il_instr::CALLVIRT => iseq.push(Instruction::CallVirt(Token(self.read_u32()?))), 35 | il_instr::BOX => iseq.push(Instruction::Box(Token(self.read_u32()?))), 36 | il_instr::NEWOBJ => iseq.push(Instruction::Newobj(Token(self.read_u32()?))), 37 | il_instr::NEWARR => iseq.push(Instruction::Newarr(Token(self.read_u32()?))), 38 | il_instr::LDC_I4_M1 => iseq.push(Instruction::Ldc_I4_M1), 39 | il_instr::LDC_I4_0 => iseq.push(Instruction::Ldc_I4_0), 40 | il_instr::LDC_I4_1 => iseq.push(Instruction::Ldc_I4_1), 41 | il_instr::LDC_I4_2 => iseq.push(Instruction::Ldc_I4_2), 42 | il_instr::LDC_I4_3 => iseq.push(Instruction::Ldc_I4_3), 43 | il_instr::LDC_I4_4 => iseq.push(Instruction::Ldc_I4_4), 44 | il_instr::LDC_I4_5 => iseq.push(Instruction::Ldc_I4_5), 45 | il_instr::LDC_I4_6 => iseq.push(Instruction::Ldc_I4_6), 46 | il_instr::LDC_I4_7 => iseq.push(Instruction::Ldc_I4_7), 47 | il_instr::LDC_I4_8 => iseq.push(Instruction::Ldc_I4_8), 48 | il_instr::LDC_I4_S => iseq.push(Instruction::Ldc_I4_S(self.read_u8()? as i32)), 49 | il_instr::LDC_I4 => iseq.push(Instruction::Ldc_I4(self.read_u32()? as i32)), 50 | il_instr::LDC_R8 => iseq.push(Instruction::Ldc_R8(unsafe { 51 | transmute::(self.read_u64()?) 52 | })), 53 | il_instr::LDARG_0 => iseq.push(Instruction::Ldarg_0), 54 | il_instr::LDARG_1 => iseq.push(Instruction::Ldarg_1), 55 | il_instr::LDARG_2 => iseq.push(Instruction::Ldarg_2), 56 | il_instr::LDARG_3 => iseq.push(Instruction::Ldarg_3), 57 | il_instr::LDARG_S => iseq.push(Instruction::Ldarg_S(self.read_u8()? as i32)), 58 | il_instr::LDLOC_0 => iseq.push(Instruction::Ldloc_0), 59 | il_instr::LDLOC_1 => iseq.push(Instruction::Ldloc_1), 60 | il_instr::LDLOC_2 => iseq.push(Instruction::Ldloc_2), 61 | il_instr::LDLOC_3 => iseq.push(Instruction::Ldloc_3), 62 | il_instr::LDLOC_S => iseq.push(Instruction::Ldloc_S(self.read_u8()?)), 63 | il_instr::LDFLD => iseq.push(Instruction::Ldfld(Token(self.read_u32()?))), 64 | il_instr::LDELEM_U1 => iseq.push(Instruction::Ldelem_U1), 65 | il_instr::LDELEM_I1 => iseq.push(Instruction::Ldelem_I1), 66 | il_instr::LDELEM_I4 => iseq.push(Instruction::Ldelem_I4), 67 | il_instr::LDELEM_REF => iseq.push(Instruction::Ldelem_ref), 68 | il_instr::STLOC_0 => iseq.push(Instruction::Stloc_0), 69 | il_instr::STLOC_1 => iseq.push(Instruction::Stloc_1), 70 | il_instr::STLOC_2 => iseq.push(Instruction::Stloc_2), 71 | il_instr::STLOC_3 => iseq.push(Instruction::Stloc_3), 72 | il_instr::STLOC_S => iseq.push(Instruction::Stloc_S(self.read_u8()?)), 73 | il_instr::STFLD => iseq.push(Instruction::Stfld(Token(self.read_u32()?))), 74 | il_instr::STELEM_I1 => iseq.push(Instruction::Stelem_I1), 75 | il_instr::STELEM_I4 => iseq.push(Instruction::Stelem_I4), 76 | il_instr::STELEM_REF => iseq.push(Instruction::Stelem_ref), 77 | il_instr::STARG_S => iseq.push(Instruction::Starg_S(self.read_u8()?)), 78 | il_instr::LDLEN => iseq.push(Instruction::Ldlen), 79 | il_instr::CONV_I4 => iseq.push(Instruction::Conv_I4), 80 | il_instr::CONV_I8 => iseq.push(Instruction::Conv_I8), 81 | il_instr::CONV_R8 => iseq.push(Instruction::Conv_R8), 82 | il_instr::CONV_R_UN => iseq.push(Instruction::Conv_R_un), 83 | il_instr::POP => iseq.push(Instruction::Pop), 84 | il_instr::DUP => iseq.push(Instruction::Dup), 85 | il_instr::BGE => iseq.push(Instruction::Bge({ 86 | let target = self.read_u32()? as i32; 87 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 88 | })), 89 | il_instr::BGE_UN => iseq.push(Instruction::Bge_un({ 90 | let target = self.read_u32()? as i32; 91 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 92 | })), 93 | il_instr::BGT => iseq.push(Instruction::Bgt({ 94 | let target = self.read_u32()? as i32; 95 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 96 | })), 97 | il_instr::BLT => iseq.push(Instruction::Blt({ 98 | let target = self.read_u32()? as i32; 99 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 100 | })), 101 | il_instr::BLE => iseq.push(Instruction::Ble({ 102 | let target = self.read_u32()? as i32; 103 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 104 | })), 105 | il_instr::BLE_UN => iseq.push(Instruction::Ble_un({ 106 | let target = self.read_u32()? as i32; 107 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 108 | })), 109 | il_instr::BEQ => iseq.push(Instruction::Beq({ 110 | let target = self.read_u32()? as i32; 111 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 112 | })), 113 | il_instr::BNE_UN => iseq.push(Instruction::Bne_un({ 114 | let target = self.read_u32()? as i32; 115 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 116 | })), 117 | il_instr::BRFALSE => iseq.push(Instruction::Brfalse({ 118 | let target = self.read_u32()? as i32; 119 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 120 | })), 121 | il_instr::BRTRUE => iseq.push(Instruction::Brtrue({ 122 | let target = self.read_u32()? as i32; 123 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 124 | })), 125 | il_instr::BR => iseq.push(Instruction::Br({ 126 | let target = self.read_u32()? as i32; 127 | *self.target_map.get(&(i as i32 + 1 + 4 + target)).unwrap() 128 | })), 129 | 0xfe => match self.iter.next()?.1 { 130 | &il_instr::CLT => iseq.push(Instruction::Clt), 131 | &il_instr::CEQ => iseq.push(Instruction::Ceq), 132 | &il_instr::CGT => iseq.push(Instruction::Cgt), 133 | _ => unimplemented!(), 134 | }, 135 | il_instr::ADD => iseq.push(Instruction::Add), 136 | il_instr::SUB => iseq.push(Instruction::Sub), 137 | il_instr::MUL => iseq.push(Instruction::Mul), 138 | il_instr::DIV => iseq.push(Instruction::Div), 139 | il_instr::REM => iseq.push(Instruction::Rem), 140 | il_instr::REM_UN => iseq.push(Instruction::Rem_un), 141 | il_instr::XOR => iseq.push(Instruction::Xor), 142 | il_instr::SHL => iseq.push(Instruction::Shl), 143 | il_instr::SHR => iseq.push(Instruction::Shr), 144 | il_instr::SHR_UN => iseq.push(Instruction::Shr_un), 145 | il_instr::NEG => iseq.push(Instruction::Neg), 146 | il_instr::RET => iseq.push(Instruction::Ret), 147 | e => unimplemented!("{:?}", e), 148 | } 149 | } 150 | 151 | Some(iseq) 152 | } 153 | 154 | fn make_target_map(&mut self) { 155 | let mut iter = self.iter.clone(); 156 | let mut iseq_size = 0; 157 | while let Some((i, byte)) = iter.next() { 158 | self.target_map.insert(i as i32, iseq_size); 159 | if *byte == 0xfe { 160 | // 2 bytes instruction 161 | let byte = iter.next().unwrap().1; 162 | for _ in 0..il_instr::get_instr2_size(*byte) - 2 { 163 | iter.next(); 164 | } 165 | } else { 166 | for _ in 0..il_instr::get_instr_size(*byte) - 1 { 167 | iter.next(); 168 | } 169 | } 170 | iseq_size += 1; 171 | } 172 | } 173 | } 174 | 175 | impl<'a> BytesToInstructions<'a> { 176 | fn read_u8(&mut self) -> Option { 177 | let x = *self.iter.next()?.1; 178 | Some(x) 179 | } 180 | 181 | fn read_u32(&mut self) -> Option { 182 | let x = *self.iter.next()?.1 as u32; 183 | let y = *self.iter.next()?.1 as u32; 184 | let z = *self.iter.next()?.1 as u32; 185 | let u = *self.iter.next()?.1 as u32; 186 | Some((u << 24) + (z << 16) + (y << 8) + x) 187 | } 188 | 189 | fn read_u64(&mut self) -> Option { 190 | let n0 = *self.iter.next()?.1 as u64; 191 | let n1 = *self.iter.next()?.1 as u64; 192 | let n2 = *self.iter.next()?.1 as u64; 193 | let n3 = *self.iter.next()?.1 as u64; 194 | let n4 = *self.iter.next()?.1 as u64; 195 | let n5 = *self.iter.next()?.1 as u64; 196 | let n6 = *self.iter.next()?.1 as u64; 197 | let n7 = *self.iter.next()?.1 as u64; 198 | Some( 199 | (n7 << 56) 200 | + (n6 << 48) 201 | + (n5 << 40) 202 | + (n4 << 32) 203 | + (n3 << 24) 204 | + (n2 << 16) 205 | + (n1 << 8) 206 | + n0, 207 | ) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/exec/instruction.rs: -------------------------------------------------------------------------------- 1 | use crate::metadata::token::*; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq)] 4 | #[allow(non_camel_case_types)] 5 | pub enum Instruction { 6 | Ldnull, 7 | Ldstr(u32), 8 | Ldc_I4_M1, 9 | Ldc_I4_0, 10 | Ldc_I4_1, 11 | Ldc_I4_2, 12 | Ldc_I4_3, 13 | Ldc_I4_4, 14 | Ldc_I4_5, 15 | Ldc_I4_6, 16 | Ldc_I4_7, 17 | Ldc_I4_8, 18 | Ldc_I4_S(i32), 19 | Ldc_I4(i32), 20 | Ldc_R8(f64), 21 | Ldarg_0, 22 | Ldarg_1, 23 | Ldarg_2, 24 | Ldarg_3, 25 | Ldarg_S(i32), 26 | Ldloc_0, 27 | Ldloc_1, 28 | Ldloc_2, 29 | Ldloc_3, 30 | Ldloc_S(u8), 31 | Ldfld(Token), 32 | Ldelem_I1, 33 | Ldelem_U1, 34 | Ldelem_I4, 35 | Ldelem_ref, 36 | Stloc_0, 37 | Stloc_1, 38 | Stloc_2, 39 | Stloc_3, 40 | Stloc_S(u8), 41 | Stfld(Token), 42 | Stelem_I1, 43 | Stelem_I4, 44 | Stelem_ref, 45 | Starg_S(u8), 46 | Ldlen, 47 | Conv_I4, 48 | Conv_I8, 49 | Conv_R8, 50 | Conv_R_un, 51 | Dup, 52 | Pop, 53 | Beq(usize), 54 | Bne_un(usize), 55 | Bge(usize), 56 | Bge_un(usize), 57 | Bgt(usize), 58 | Blt(usize), 59 | Ble(usize), 60 | Ble_un(usize), 61 | Brfalse(usize), 62 | Brtrue(usize), 63 | Br(usize), 64 | Cgt, 65 | Clt, 66 | Ceq, 67 | Add, 68 | Sub, 69 | Mul, 70 | Div, 71 | Rem, 72 | Rem_un, 73 | Xor, 74 | Shl, 75 | Shr, 76 | Shr_un, 77 | Neg, 78 | Call(Token), 79 | CallVirt(Token), 80 | Box(Token), 81 | Newobj(Token), 82 | Newarr(Token), 83 | Ret, 84 | } 85 | 86 | #[rustfmt::skip] 87 | pub mod il_instr { 88 | pub const LDNULL : u8 = 0x14; 89 | pub const LDSTR : u8 = 0x72; 90 | pub const CALL : u8 = 0x28; 91 | pub const CALLVIRT : u8 = 0x6f; 92 | pub const LDC_I4_M1 : u8 = 0x15; 93 | pub const LDC_I4_0 : u8 = 0x16; 94 | pub const LDC_I4_1 : u8 = 0x17; 95 | pub const LDC_I4_2 : u8 = 0x18; 96 | pub const LDC_I4_3 : u8 = 0x19; 97 | pub const LDC_I4_4 : u8 = 0x1a; 98 | pub const LDC_I4_5 : u8 = 0x1b; 99 | pub const LDC_I4_6 : u8 = 0x1c; 100 | pub const LDC_I4_7 : u8 = 0x1d; 101 | pub const LDC_I4_8 : u8 = 0x1e; 102 | pub const LDC_I4_S : u8 = 0x1f; 103 | pub const LDC_I4 : u8 = 0x20; 104 | pub const LDC_R8 : u8 = 0x23; 105 | pub const LDARG_0 : u8 = 0x02; 106 | pub const LDARG_1 : u8 = 0x03; 107 | pub const LDARG_2 : u8 = 0x04; 108 | pub const LDARG_3 : u8 = 0x05; 109 | pub const LDARG_S : u8 = 0x0e; 110 | pub const LDLOC_0 : u8 = 0x06; 111 | pub const LDLOC_1 : u8 = 0x07; 112 | pub const LDLOC_2 : u8 = 0x08; 113 | pub const LDLOC_3 : u8 = 0x09; 114 | pub const LDLOC_S : u8 = 0x11; 115 | pub const LDFLD : u8 = 0x7b; 116 | pub const LDELEM_I1 : u8 = 0x90; 117 | pub const LDELEM_U1 : u8 = 0x91; 118 | pub const LDELEM_I4 : u8 = 0x94; 119 | pub const LDELEM_REF : u8 = 0x9a; 120 | pub const STLOC_0 : u8 = 0x0a; 121 | pub const STLOC_1 : u8 = 0x0b; 122 | pub const STLOC_2 : u8 = 0x0c; 123 | pub const STLOC_3 : u8 = 0x0d; 124 | pub const STLOC_S : u8 = 0x13; 125 | pub const STFLD : u8 = 0x7d; 126 | pub const STELEM_I1 : u8 = 0x9c; 127 | pub const STELEM_I4 : u8 = 0x9e; 128 | pub const STELEM_REF : u8 = 0xa2; 129 | pub const STARG_S : u8 = 0x10; 130 | pub const LDLEN : u8 = 0x8e; 131 | pub const CONV_I4 : u8 = 0x69; 132 | pub const CONV_I8 : u8 = 0x6a; 133 | pub const CONV_R8 : u8 = 0x6c; 134 | pub const CONV_R_UN : u8 = 0x76; 135 | pub const DUP : u8 = 0x25; 136 | pub const POP : u8 = 0x26; 137 | pub const BR : u8 = 0x38; 138 | pub const BGE : u8 = 0x3c; 139 | pub const BGE_UN : u8 = 0x41; 140 | pub const BGT : u8 = 0x3d; 141 | pub const BLE : u8 = 0x3e; 142 | pub const BLE_UN : u8 = 0x43; 143 | pub const BLT : u8 = 0x3f; 144 | pub const BEQ : u8 = 0x3b; 145 | pub const BNE_UN : u8 = 0x40; 146 | pub const BRFALSE : u8 = 0x39; 147 | pub const BRTRUE : u8 = 0x3a; 148 | pub const CGT : u8 = 0x02; // 0xfe leads 149 | pub const CLT : u8 = 0x04; // 0xfe leads 150 | pub const CEQ : u8 = 0x01; // 0xfe leads 151 | pub const ADD : u8 = 0x58; 152 | pub const SUB : u8 = 0x59; 153 | pub const MUL : u8 = 0x5a; 154 | pub const DIV : u8 = 0x5b; 155 | pub const REM : u8 = 0x5d; 156 | pub const REM_UN : u8 = 0x5e; 157 | pub const XOR : u8 = 0x61; 158 | pub const SHL : u8 = 0x62; 159 | pub const SHR : u8 = 0x63; 160 | pub const SHR_UN : u8 = 0x64; 161 | pub const NEG : u8 = 0x65; 162 | pub const BOX : u8 = 0x8c; 163 | pub const NEWOBJ : u8 = 0x73; 164 | pub const NEWARR : u8 = 0x8d; 165 | pub const RET : u8 = 0x2a; 166 | 167 | pub fn get_instr_size<'a>(instr: u8) -> usize { 168 | match instr { 169 | LDC_R8 => 9, 170 | LDNULL | LDSTR | 171 | CALL | CALLVIRT | 172 | NEWOBJ | NEWARR | BOX | 173 | STFLD | LDFLD | 174 | BGE | BGE_UN | BR | BLT | BNE_UN | BRFALSE | BGT 175 | | BRTRUE | BLE | BLE_UN | BEQ | 176 | LDC_I4 => 5, 177 | LDC_I4_M1 | LDC_I4_0 | LDC_I4_1 | LDC_I4_2 | LDC_I4_3 178 | | LDC_I4_4 | LDC_I4_5 | LDC_I4_6 179 | | LDC_I4_7 | LDC_I4_8 | 180 | LDARG_0 | LDARG_1 | LDARG_2 | LDARG_3 | 181 | LDLOC_0 | LDLOC_1 | LDLOC_2 | LDLOC_3 | 182 | LDELEM_I4 | LDELEM_I1 | LDELEM_U1 | LDELEM_REF | 183 | STLOC_0 | STLOC_1 | STLOC_2 | STLOC_3 | 184 | STELEM_I4 | STELEM_I1 | STELEM_REF | 185 | ADD | SUB | MUL | DIV | REM | REM_UN | XOR 186 | | SHL | SHR | SHR_UN | NEG | 187 | RET | POP | DUP | 188 | CONV_I4 | CONV_I8 | CONV_R8 | CONV_R_UN | 189 | LDLEN => 1, 190 | LDLOC_S | 191 | STLOC_S | 192 | STARG_S | LDARG_S | LDC_I4_S => 2, 193 | e => panic!("Not an instruction: {}", e), 194 | } 195 | } 196 | 197 | pub fn get_instr2_size<'a>(instr: u8) -> usize { 198 | match instr { 199 | CGT | CLT | CEQ => 2, 200 | e => panic!("2 bytes inst: Not an instruction: {}", e), 201 | } 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/exec/jit/builtin.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | exec::jit::jit::*, 3 | metadata::signature::*, 4 | util::{name_path::*, resolver::*}, 5 | }; 6 | use llvm::{core::*, prelude::*}; 7 | use rustc_hash::FxHashMap; 8 | use std::{ 9 | char::decode_utf16, 10 | ffi::{c_void, CString}, 11 | mem, ptr, 12 | }; 13 | 14 | #[derive(Clone, Debug)] 15 | pub struct Function { 16 | pub ty: Type, 17 | pub function: *mut c_void, 18 | pub llvm_function: LLVMValueRef, 19 | } 20 | 21 | #[derive(Clone)] 22 | pub struct BuiltinFunctions { 23 | pub map: NameResolver>, 24 | pub helper_map: FxHashMap, 25 | } 26 | 27 | impl BuiltinFunctions { 28 | #[rustfmt::skip] 29 | pub unsafe fn new(ctx: LLVMContextRef, module: LLVMModuleRef) -> Self { 30 | Self { 31 | helper_map: { 32 | let mut map = FxHashMap::default(); 33 | map.insert( 34 | "memory_alloc".to_string(), 35 | Function { 36 | ty: Type::void_ty(), 37 | function: memory_alloc as *mut c_void, 38 | llvm_function: LLVMAddFunction( 39 | module, 40 | CString::new("memory_alloc").unwrap().as_ptr(), 41 | LLVMFunctionType( 42 | LLVMPointerType(LLVMInt8TypeInContext(ctx), 0), 43 | vec![LLVMInt32TypeInContext(ctx)].as_mut_ptr(), 44 | 1, 0)) 45 | } 46 | ); 47 | map.insert( 48 | "new_szarray".to_string(), 49 | Function { 50 | ty: Type::void_ty(), 51 | function: new_szarray as *mut c_void, 52 | llvm_function: LLVMAddFunction( 53 | module, 54 | CString::new("new_szarray").unwrap().as_ptr(), 55 | LLVMFunctionType( 56 | LLVMPointerType(LLVMInt8TypeInContext(ctx), 0), 57 | vec![LLVMInt32TypeInContext(ctx), 58 | LLVMInt32TypeInContext(ctx) 59 | ].as_mut_ptr(), 60 | 2, 0)) 61 | } 62 | ); 63 | map 64 | }, 65 | map: { 66 | macro_rules! parse_llvm_ty { 67 | (void) => { LLVMVoidTypeInContext(ctx) }; 68 | (i4 ) => { LLVMInt32TypeInContext(ctx) }; 69 | (r8 ) => { LLVMDoubleTypeInContext(ctx) }; 70 | (char) => { LLVMInt32TypeInContext(ctx) }; 71 | (str ) => { LLVMPointerType(LLVMInt8TypeInContext(ctx), 0) }; 72 | (obj ) => { LLVMPointerType(LLVMInt8TypeInContext(ctx), 0) }; 73 | (obja) => { LLVMPointerType(LLVMInt8TypeInContext(ctx), 0) }; 74 | (ptr ) => { LLVMPointerType(LLVMInt8TypeInContext(ctx), 0) }; 75 | } 76 | 77 | macro_rules! parse_ty { 78 | (void) => { Type::void_ty() }; 79 | (i4 ) => { Type::i4_ty() }; 80 | (r8 ) => { Type::r8_ty() }; 81 | (char) => { Type::char_ty() }; 82 | (obj ) => { Type::object_ty() }; 83 | (obja) => { Type::object_szarr_ty() }; 84 | (str ) => { Type::string_ty() }; 85 | } 86 | 87 | macro_rules! def_func { 88 | ($ret_ty:ident, [ $($param_ty:ident),* ], $f:expr, $name:expr) => {{ 89 | def_func!([0], $ret_ty, [$($param_ty),*], $f, $name) 90 | }}; 91 | 92 | ([$flags:expr], $ret_ty:ident, [ $($param_ty:ident),* ], $f:expr, $name:expr) => {{ 93 | let mut llvm_params_ty = if $flags & 0x20 > 0 { vec![parse_llvm_ty!(ptr)] } else { vec![] }; 94 | llvm_params_ty.append(&mut vec![$(parse_llvm_ty!($param_ty)),*]); 95 | let params_ty = vec![$(parse_ty!($param_ty)),*]; 96 | let func_ty = LLVMFunctionType( 97 | parse_llvm_ty!($ret_ty), 98 | llvm_params_ty.as_mut_ptr(), 99 | llvm_params_ty.len() as u32, 0); 100 | (Type::full_method_ty($flags, parse_ty!($ret_ty), ¶ms_ty), 101 | $f as *mut c_void, 102 | LLVMAddFunction(module, CString::new($name).unwrap().as_ptr(), func_ty)) 103 | }} 104 | } 105 | 106 | let sqrt = vec![ 107 | def_func!( r8, [r8 ], 0, "llvm.sqrt.f64") 108 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 109 | let abs = vec![ 110 | def_func!( r8, [r8 ], 0, "llvm.fabs.f64") 111 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 112 | let cos = vec![ 113 | def_func!( r8, [r8 ], 0, "llvm.cos.f64") 114 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 115 | let sin = vec![ 116 | def_func!( r8, [r8 ], 0, "llvm.sin.f64") 117 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 118 | let pow = vec![ 119 | def_func!( r8, [r8, r8], 0, "llvm.pow.f64") 120 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 121 | let write_line = vec![ 122 | def_func!( void, [str ], write_line_string, "[mscorlib]System::Console.WriteLine(String)"), 123 | def_func!( void, [i4 ], write_line_i4, "[mscorlib]System::Console.WriteLine(int32)"), 124 | def_func!( void, [r8 ], write_line_r8, "[mscorlib]System::Console.WriteLine(float64)"), 125 | def_func!( void, [char], write_line_char, "[mscorlib]System::Console.WriteLine(char)"), 126 | def_func!( void, [str, obj], write_line_string_obj, "[mscorlib]System::Console.WriteLine(String, Object)"), 127 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 128 | let write = vec![ 129 | def_func!( void, [str ], write_string, "[mscorlib]System::Console.Write(String)"), 130 | def_func!( void, [i4 ], write_i4, "[mscorlib]System::Console.Write(int32)"), 131 | def_func!( void, [r8 ], write_r8, "[mscorlib]System::Console.Write(float64)"), 132 | def_func!( void, [char], write_char, "[mscorlib]System::Console.Write(char)"), 133 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 134 | let get_length = vec![ 135 | def_func!([0x20], i4 , [], get_length, "[mscorlib]System::String.get_Length()") 136 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function}).collect(); 137 | let get_chars = vec![ 138 | def_func!([0x20], char, [i4], get_chars_i4, "[mscorlib]System::String.get_Chars(int32)") 139 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 140 | let concat = vec![ 141 | def_func!( str, [obja], concat_obj_arr, "[mscorlib]System::String.Concat(Object[])"), 142 | def_func!( str, [obj, obj], concat_obj_obj, "[mscorlib]System::String.Concat(Object, Object)"), 143 | def_func!( str, [str, str], concat_str_str, "[mscorlib]System::String.Concat(String, String)"), 144 | def_func!( str, [obj, obj, obj], concat_obj_obj_obj, "[mscorlib]System::String.Concat(Object, Object, Object)") 145 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 146 | let int32_to_string = vec![ 147 | def_func!([0x20], str, [], int_to_string, "[mscorlib]System::Int32.ToString()") 148 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 149 | let obj_to_string = vec![ 150 | def_func!([0x20], str, [], object_to_string, "[mscorlib]System::Object.ToString()"), 151 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 152 | let string_to_string = vec![ 153 | def_func!([0x20], str, [], string_to_string, "[mscorlib]System::String.ToString()"), 154 | ].into_iter().map(|(ty, function, llvm_function)| Function { ty, function, llvm_function }).collect(); 155 | 156 | let mut resolver = NameResolver::new(); 157 | 158 | resolver.add(MethodPath(vec!["mscorlib", "System", "Console", "WriteLine" ]), write_line ); 159 | resolver.add(MethodPath(vec!["mscorlib", "System", "Console", "Write" ]), write ); 160 | resolver.add(MethodPath(vec!["mscorlib", "System", "Object", "ToString" ]), obj_to_string ); 161 | resolver.add(MethodPath(vec!["mscorlib", "System", "Int32", "ToString" ]), int32_to_string ); 162 | resolver.add(MethodPath(vec!["mscorlib", "System", "String", "get_Chars" ]), get_chars ); 163 | resolver.add(MethodPath(vec!["mscorlib", "System", "String", "get_Length"]), get_length ); 164 | resolver.add(MethodPath(vec!["mscorlib", "System", "String", "ToString" ]), string_to_string); 165 | resolver.add(MethodPath(vec!["mscorlib", "System", "String", "Concat" ]), concat ); 166 | resolver.add(MethodPath(vec!["mscorlib", "System", "Math", "Sqrt" ]), sqrt ); 167 | resolver.add(MethodPath(vec!["mscorlib", "System", "Math", "Sin" ]), sin ); 168 | resolver.add(MethodPath(vec!["mscorlib", "System", "Math", "Cos" ]), cos ); 169 | resolver.add(MethodPath(vec!["mscorlib", "System", "Math", "Abs" ]), abs ); 170 | resolver.add(MethodPath(vec!["mscorlib", "System", "Math", "Pow" ]), pow ); 171 | 172 | resolver 173 | }, 174 | } 175 | } 176 | 177 | pub fn get_method<'a, T: Into>>(&self, path: T, ty: &Type) -> Option<&Function> { 178 | let methods = self.map.get(path.into())?; 179 | methods.iter().find(|f| &f.ty == ty) 180 | } 181 | 182 | pub fn get_helper_function(&self, name: &str) -> Option<&Function> { 183 | self.helper_map.get(name) 184 | } 185 | 186 | pub fn list_all_function(&self) -> Vec { 187 | let mut functions: Vec = 188 | self.map.collect_values().into_iter().flatten().collect(); 189 | functions.append(&mut self.helper_map.iter().map(|(_, f)| f.clone()).collect()); 190 | functions 191 | } 192 | } 193 | 194 | #[no_mangle] 195 | pub fn write_line_i4(n: i32) { 196 | println!("{}", n); 197 | } 198 | 199 | #[no_mangle] 200 | pub fn write_line_r8(n: f64) { 201 | println!("{}", n); 202 | } 203 | 204 | #[no_mangle] 205 | pub fn write_line_char(c: u16) { 206 | println!( 207 | "{}", 208 | decode_utf16([c].iter().cloned()).next().unwrap().unwrap() 209 | ); 210 | } 211 | 212 | #[no_mangle] 213 | pub fn write_line_string(system_string: *mut u64) { 214 | let utf16_string_ptr = unsafe { retrieve_utf16_string_from_system_string(system_string) }; 215 | println!( 216 | "{}", 217 | String::from_utf16_lossy(unsafe { &*utf16_string_ptr }) 218 | ); 219 | } 220 | 221 | unsafe fn retrieve_utf16_string_from_system_string(system_string: *mut u64) -> *mut Vec { 222 | *system_string.offset(1) as *mut Vec 223 | } 224 | 225 | unsafe fn new_system_string(s: String) -> *mut u64 { 226 | let system_string = memory_alloc(16) as *mut u64; 227 | *(system_string.offset(0) as *mut MethodTablePtrTy) = 228 | STRING_METHOD_TABLE_PTR.with(|smp| smp.borrow().unwrap()); 229 | *(system_string.offset(1) as *mut *mut Vec) = new_utf16_string(s); 230 | system_string 231 | } 232 | 233 | unsafe fn new_system_string_from_utf16(s: Vec) -> *mut u64 { 234 | let system_string = memory_alloc(16) as *mut u64; 235 | *(system_string.offset(0) as *mut MethodTablePtrTy) = 236 | STRING_METHOD_TABLE_PTR.with(|smp| smp.borrow().unwrap()); 237 | *(system_string.offset(1) as *mut *mut Vec) = new_utf16_string_from_vec_u16(s); 238 | system_string 239 | } 240 | 241 | unsafe fn get_virtual_to_string_method(obj: *mut u64) -> fn(*mut u64) -> *mut u64 { 242 | let method_table = *obj.offset(0) as *mut u64; 243 | mem::transmute:: *mut u64>(*method_table.offset(0)) 244 | } 245 | 246 | unsafe fn convert_object_to_string(obj: *mut u64) -> String { 247 | String::from_utf16_lossy(&*retrieve_utf16_string_from_system_string( 248 | (get_virtual_to_string_method(obj))(obj), 249 | )) 250 | } 251 | 252 | #[no_mangle] 253 | unsafe fn concat_str_str(s1: *mut u64, s2: *mut u64) -> *mut u64 { 254 | let mut s1 = (&*retrieve_utf16_string_from_system_string(s1)).clone(); 255 | let s2 = &*retrieve_utf16_string_from_system_string(s2); 256 | s1.extend(s2); 257 | // s1.push_str(s2.as_str()); 258 | new_system_string_from_utf16(s1) 259 | } 260 | 261 | #[no_mangle] 262 | unsafe fn concat_obj_arr(objs: *mut u64) -> *mut u64 { 263 | let len = *objs; 264 | let objs = objs.add(1) as *mut u64; 265 | let mut res = "".to_string(); 266 | for i in 0..len { 267 | let system_object = *objs.add(i as usize) as *mut u64; 268 | let s = convert_object_to_string(system_object); 269 | res.push_str(s.as_str()); 270 | } 271 | new_system_string(res) 272 | } 273 | 274 | #[no_mangle] 275 | unsafe fn concat_obj_obj(obj1: *mut u64, obj2: *mut u64) -> *mut u64 { 276 | let mut s1 = convert_object_to_string(obj1); 277 | let s2 = convert_object_to_string(obj2); 278 | s1.push_str(s2.as_str()); 279 | new_system_string(s1) 280 | } 281 | 282 | #[no_mangle] 283 | unsafe fn concat_obj_obj_obj(obj1: *mut u64, obj2: *mut u64, obj3: *mut u64) -> *mut u64 { 284 | let mut s1 = convert_object_to_string(obj1); 285 | let s2 = convert_object_to_string(obj2); 286 | let s3 = convert_object_to_string(obj3); 287 | s1.push_str(s2.as_str()); 288 | s1.push_str(s3.as_str()); 289 | new_system_string(s1) 290 | } 291 | 292 | #[no_mangle] 293 | pub unsafe fn write_line_string_obj(system_string: *mut u64, system_int32: *mut u64) { 294 | let string = 295 | String::from_utf16_lossy({ &*retrieve_utf16_string_from_system_string(system_string) }); 296 | let to_string = get_virtual_to_string_method(system_int32); 297 | let string2 = String::from_utf16_lossy(&*retrieve_utf16_string_from_system_string(to_string( 298 | system_int32, 299 | ))); 300 | for (i, s) in string.split("{0}").enumerate() { 301 | if i > 0 { 302 | print!("{}", string2) 303 | } 304 | print!("{}", s); 305 | } 306 | println!(); 307 | } 308 | 309 | #[no_mangle] 310 | pub fn write_i4(n: i32) { 311 | print!("{}", n); 312 | } 313 | 314 | #[no_mangle] 315 | pub fn write_r8(n: f64) { 316 | print!("{}", n); 317 | } 318 | 319 | #[no_mangle] 320 | pub fn write_char(c: u16) { 321 | print!( 322 | "{}", 323 | decode_utf16([c].iter().cloned()).next().unwrap().unwrap() 324 | ); 325 | } 326 | 327 | #[no_mangle] 328 | pub fn write_string(system_string: *mut u64) { 329 | let utf16_string_ptr = unsafe { retrieve_utf16_string_from_system_string(system_string) }; 330 | print!( 331 | "{}", 332 | String::from_utf16_lossy(unsafe { &*utf16_string_ptr }) 333 | ); 334 | } 335 | 336 | #[no_mangle] 337 | pub fn get_length(system_string: *mut u64) -> i32 { 338 | let string_ptr = unsafe { retrieve_utf16_string_from_system_string(system_string) }; 339 | unsafe { &*string_ptr }.len() as i32 340 | } 341 | 342 | #[no_mangle] 343 | pub fn get_chars_i4(system_string: *mut u64, i: i32) -> i32 { 344 | let string_ptr = unsafe { retrieve_utf16_string_from_system_string(system_string) }; 345 | (unsafe { &*string_ptr })[i as usize] as i32 346 | } 347 | 348 | #[no_mangle] 349 | pub unsafe fn object_to_string(_obj: *mut u8) -> *mut u64 { 350 | new_system_string("[unimplemented]".to_string()) 351 | } 352 | 353 | #[no_mangle] 354 | pub fn int_to_string(system_int32: *mut u64) -> *mut u64 { 355 | unsafe { 356 | let value = *system_int32.offset(1) as i32; 357 | new_system_string(format!("{}", value)) 358 | } 359 | } 360 | 361 | #[no_mangle] 362 | pub fn string_to_string(string: *mut u8) -> *mut u8 { 363 | string 364 | } 365 | 366 | // TODO: Currently using boehm-gc. Replace with better way in the future. 367 | #[link(name = "gc")] 368 | extern "C" { 369 | fn GC_malloc(len: u32) -> *mut u8; 370 | fn GC_register_finalizer(obj: *mut u8, f: *mut u8, cd: *mut u8, ofn: *mut u8, ocd: *mut u8); 371 | } 372 | 373 | unsafe fn new_utf16_string(s: String) -> *mut Vec { 374 | let utf16 = s.encode_utf16().collect::>(); 375 | let ptr = GC_malloc(mem::size_of::>() as u32); 376 | ptr::copy_nonoverlapping(&utf16 as *const Vec, ptr as *mut Vec, 1); 377 | mem::forget(utf16); 378 | GC_register_finalizer( 379 | ptr, 380 | finalizer_string as *mut u8, 381 | 0 as *mut u8, 382 | 0 as *mut u8, 383 | 0 as *mut u8, 384 | ); 385 | ptr as *mut Vec 386 | } 387 | 388 | pub unsafe fn new_utf16_string_from_vec_u16(utf16: Vec) -> *mut Vec { 389 | let ptr = GC_malloc(mem::size_of::>() as u32); 390 | ptr::copy_nonoverlapping(&utf16 as *const Vec, ptr as *mut Vec, 1); 391 | mem::forget(utf16); 392 | GC_register_finalizer( 393 | ptr, 394 | finalizer_string as *mut u8, 395 | 0 as *mut u8, 396 | 0 as *mut u8, 397 | 0 as *mut u8, 398 | ); 399 | ptr as *mut Vec 400 | } 401 | 402 | #[no_mangle] 403 | fn finalizer_string(obj: *mut Vec, _cd: *mut u8) { 404 | unsafe { ptr::drop_in_place(obj) } 405 | } 406 | 407 | #[no_mangle] 408 | pub fn memory_alloc(len: u32) -> *mut u8 { 409 | unsafe { GC_malloc(len) } 410 | } 411 | 412 | #[no_mangle] 413 | pub fn new_szarray(elem_sz: u32, len: u32) -> *mut u8 { 414 | let ptr = unsafe { GC_malloc(elem_sz * len + 8) }; 415 | unsafe { *(ptr as *mut u64) = len as u64 }; 416 | ptr 417 | } 418 | -------------------------------------------------------------------------------- /src/exec/jit/cfg.rs: -------------------------------------------------------------------------------- 1 | use crate::exec::instruction::*; 2 | use std::collections::BTreeMap; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct BasicBlock { 6 | pub code: Vec, 7 | pub start: usize, 8 | pub kind: BrKind, 9 | } 10 | 11 | #[derive(Clone, Debug, PartialEq)] 12 | pub enum BrKind { 13 | ConditionalJmp { destinations: Vec }, 14 | UnconditionalJmp { destination: usize }, 15 | ImplicitJmp { destination: usize }, 16 | BlockStart, 17 | } 18 | 19 | #[derive(Debug, Clone)] 20 | pub struct CFGMaker {} 21 | 22 | impl CFGMaker { 23 | pub fn new() -> Self { 24 | CFGMaker {} 25 | } 26 | } 27 | 28 | impl CFGMaker { 29 | pub fn make_basic_blocks(&mut self, code: &[Instruction]) -> Vec { 30 | let mut map = BTreeMap::new(); 31 | 32 | macro_rules! jmp_at { 33 | ($k:expr, $v:expr) => {{ 34 | map.entry($k).or_insert_with(|| vec![]).push($v) 35 | }}; 36 | } 37 | 38 | macro_rules! new_block_starts_at { 39 | ($k:expr) => {{ 40 | map.entry($k) 41 | .or_insert_with(|| vec![]) 42 | .push(BrKind::BlockStart) 43 | }}; 44 | } 45 | 46 | for (pc, instr) in code.iter().enumerate() { 47 | match instr { 48 | Instruction::Bge(target) 49 | | Instruction::Bge_un(target) 50 | | Instruction::Bgt(target) 51 | | Instruction::Ble(target) 52 | | Instruction::Ble_un(target) 53 | | Instruction::Blt(target) 54 | | Instruction::Beq(target) 55 | | Instruction::Bne_un(target) 56 | | Instruction::Brfalse(target) 57 | | Instruction::Brtrue(target) => { 58 | jmp_at!( 59 | pc, 60 | BrKind::ConditionalJmp { 61 | destinations: vec![*target, pc + 1] 62 | } 63 | ); 64 | new_block_starts_at!(*target); 65 | new_block_starts_at!(pc + 1); 66 | } 67 | Instruction::Br(target) => { 68 | jmp_at!( 69 | pc, 70 | BrKind::UnconditionalJmp { 71 | destination: *target, 72 | } 73 | ); 74 | new_block_starts_at!(*target); 75 | } 76 | _ => {} 77 | } 78 | } 79 | 80 | let mut start = Some(0); 81 | let mut blocks = vec![]; 82 | 83 | macro_rules! create_block { 84 | ($range:expr, $kind:expr) => {{ 85 | blocks.push(BasicBlock { 86 | code: code[$range].to_vec(), 87 | start: $range.start, 88 | kind: $kind, 89 | }); 90 | }}; 91 | } 92 | 93 | for (key, kind_list) in map { 94 | for kind in kind_list { 95 | match kind { 96 | BrKind::BlockStart => { 97 | if let Some(start) = start { 98 | if start < key { 99 | create_block!(start..key, BrKind::ImplicitJmp { destination: key }) 100 | } 101 | } 102 | start = Some(key) 103 | } 104 | BrKind::ConditionalJmp { .. } | BrKind::UnconditionalJmp { .. } => { 105 | if let Some(start) = start { 106 | if start <= key { 107 | create_block!(start..key + 1, kind) 108 | } 109 | } 110 | start = None; 111 | } 112 | _ => {} 113 | } 114 | } 115 | } 116 | 117 | if let Some(start) = start { 118 | create_block!(start..code.len(), BrKind::BlockStart); 119 | } 120 | 121 | blocks 122 | } 123 | } 124 | 125 | impl BrKind { 126 | pub fn get_conditional_jump_destinations(&self) -> &Vec { 127 | match self { 128 | BrKind::ConditionalJmp { destinations } => destinations, 129 | _ => panic!(), 130 | } 131 | } 132 | 133 | pub fn get_unconditional_jump_destination(&self) -> usize { 134 | match self { 135 | BrKind::UnconditionalJmp { destination } => *destination, 136 | BrKind::ImplicitJmp { destination } => *destination, 137 | _ => panic!(), 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/exec/jit/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod builtin; 2 | pub mod cfg; 3 | pub mod jit; 4 | -------------------------------------------------------------------------------- /src/exec/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod decode; 2 | pub mod instruction; 3 | // pub mod interpret; 4 | pub mod jit; 5 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(concat_idents)] 2 | #![feature(type_ascription)] 3 | #[macro_use] 4 | pub mod macros; 5 | pub mod exec; 6 | pub mod metadata; 7 | pub mod util; 8 | 9 | extern crate id_arena; 10 | extern crate llvm_sys as llvm; 11 | extern crate rustc_hash; 12 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! dprintln { 3 | ($($arg:tt)*) => { 4 | #[cfg(debug_assertions)] 5 | { 6 | let s = format!($($arg)*); 7 | println!("{}", ansi_term::Colour::White.dimmed().paint(s)); 8 | } 9 | } 10 | } 11 | 12 | #[macro_export] 13 | macro_rules! dprint { 14 | ($($arg:tt)*) => { 15 | #[cfg(debug_assertions)] 16 | { 17 | let s = format!($($arg)*); 18 | print!("{}", ansi_term::Colour::White.dimmed().paint(s)); 19 | } 20 | } 21 | } 22 | 23 | #[macro_export] 24 | macro_rules! when_debug { 25 | ($($arg:tt)*) => { 26 | #[cfg(debug_assertions)] 27 | { 28 | $($arg)*; 29 | } 30 | }; 31 | } 32 | 33 | #[macro_export] 34 | macro_rules! retrieve { 35 | ($e:expr,$p:path) => {{ 36 | match $e { 37 | $p(x) => x, 38 | _ => panic!(), 39 | } 40 | }}; 41 | } 42 | 43 | #[macro_export] 44 | macro_rules! matches { 45 | ($e:expr, $p:pat) => { 46 | match $e { 47 | $p => true, 48 | _ => false, 49 | } 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate yacht; 2 | use yacht::{exec::jit, metadata::assembly}; 3 | 4 | use std::path::PathBuf; 5 | 6 | extern crate clap; 7 | use clap::{App, AppSettings, Arg}; 8 | 9 | extern crate ansi_term; 10 | use ansi_term::Colour; 11 | 12 | const VERSION_STR: &'static str = env!("CARGO_PKG_VERSION"); 13 | 14 | fn main() { 15 | let app = App::new("Yacht") 16 | .version(VERSION_STR) 17 | .author("uint256_t") 18 | .about("An ECMA-335 implementation written in Rust") 19 | .arg(Arg::with_name("file").help("Input file name").index(1)) 20 | .setting(AppSettings::ArgRequiredElseHelp); 21 | let app_matches = app.get_matches(); 22 | 23 | let filename = match app_matches.value_of("file") { 24 | Some(filename) => PathBuf::from(filename), 25 | None => return, 26 | }; 27 | 28 | #[rustfmt::skip] 29 | macro_rules! expect { ($expr:expr, $msg:expr) => {{ match $expr { 30 | Some(some) => some, 31 | None => { eprintln!("{}: {}", Colour::Red.bold().paint("error"), $msg); return } 32 | } }}; } 33 | 34 | let asm = expect!( 35 | assembly::Assembly::load(filename), 36 | "An error occurred while loading file" 37 | ); 38 | let entry_method = expect!( 39 | asm.borrow_mut().image.get_entry_method(), 40 | "Entry method not found" 41 | ); 42 | 43 | unsafe { 44 | let mut asm = asm.borrow_mut(); 45 | let mut shared_env = jit::jit::SharedEnvironment::new(); 46 | let mut jit = jit::jit::JITCompiler::new(&mut *asm, &mut shared_env); 47 | let main = jit.generate_method_as_main(&entry_method); 48 | jit.run_method(main); 49 | } 50 | } 51 | 52 | #[cfg(test)] 53 | mod tests { 54 | use std::{fs, path::PathBuf}; 55 | use yacht::{exec::jit, metadata::assembly}; 56 | 57 | #[test] 58 | fn exec_examples() { 59 | let paths = fs::read_dir("./examples").unwrap(); 60 | for entry in paths { 61 | let path = entry.unwrap().path(); 62 | let filename = path.to_str().unwrap(); 63 | if !filename.ends_with(".exe") || filename.ends_with("smallpt.exe") { 64 | continue; 65 | } 66 | let asm = assembly::Assembly::load(PathBuf::from(filename)).unwrap(); 67 | let method = asm.borrow_mut().image.get_entry_method().unwrap(); 68 | unsafe { 69 | let mut asm = asm.borrow_mut(); 70 | let mut shared_env = jit::jit::SharedEnvironment::new(); 71 | let mut jit = jit::jit::JITCompiler::new(&mut asm, &mut shared_env); 72 | let main = jit.generate_method_as_main(&method); 73 | jit.run_method(main); 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/metadata/assembly.rs: -------------------------------------------------------------------------------- 1 | use super::{image::*, pe_parser::*}; 2 | use rustc_hash::FxHashMap; 3 | use std::{cell::RefCell, path::PathBuf, rc::Rc}; 4 | 5 | pub type AssemblyRef = Rc>; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Assembly { 9 | pub name: String, 10 | pub image: Image, 11 | } 12 | 13 | impl Assembly { 14 | pub fn load(filename: PathBuf) -> Option { 15 | let mut pe_parser = PEParser::new(filename)?; 16 | let asmref = Rc::new(RefCell::new(pe_parser.create_assembly()?)); 17 | let mut loaded = FxHashMap::default(); 18 | 19 | loaded.insert(asmref.borrow().name.clone(), asmref.clone()); 20 | 21 | { 22 | let mut asm = asmref.borrow_mut(); 23 | asm.image.pe_parser = Some(Rc::new(RefCell::new(pe_parser))); 24 | asm.image.setup_all_asmref(&mut loaded); 25 | asm.image.define_all_class(); 26 | asm.image.setup_all_typeref(); 27 | asm.image.setup_all_class(); 28 | } 29 | 30 | for asm in loaded.values() { 31 | let mut ok = asm.borrow_mut(); 32 | ok.image.setup_all_typeref(); 33 | ok.image.setup_all_class(); 34 | } 35 | 36 | Some(asmref) 37 | } 38 | 39 | pub fn load_exclusive( 40 | filename: PathBuf, 41 | loaded: &mut FxHashMap, 42 | ) -> Option { 43 | let mut pe_parser = PEParser::new(filename)?; 44 | let asmref = Rc::new(RefCell::new(pe_parser.create_assembly()?)); 45 | 46 | loaded.insert(asmref.borrow().name.clone(), asmref.clone()); 47 | 48 | { 49 | let mut asm = asmref.borrow_mut(); 50 | asm.image.pe_parser = Some(Rc::new(RefCell::new(pe_parser))); 51 | asm.image.setup_all_asmref(loaded); 52 | asm.image.define_all_class(); 53 | } 54 | 55 | Some(asmref) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/metadata/class.rs: -------------------------------------------------------------------------------- 1 | use crate::metadata::{method::*, signature::*}; 2 | use std::{cell::RefCell, fmt, rc::Rc}; 3 | 4 | pub type ClassInfoRef = Rc>; 5 | 6 | #[derive(Clone)] 7 | pub struct ClassInfo { 8 | pub resolution_scope: ResolutionScope, 9 | pub name: String, 10 | pub namespace: String, 11 | pub fields: Vec, 12 | pub methods: Vec, 13 | pub parent: Option, 14 | pub method_table: Vec, 15 | } 16 | 17 | #[derive(Clone, PartialEq, Debug)] 18 | pub struct ClassField { 19 | pub name: String, 20 | pub ty: Type, 21 | } 22 | 23 | #[derive(Clone, PartialEq, Debug)] 24 | pub enum ResolutionScope { 25 | AssemblyRef { name: String }, 26 | None, // TODO 27 | } 28 | 29 | impl PartialEq for ClassInfo { 30 | fn eq(&self, other: &Self) -> bool { 31 | self.resolution_scope == other.resolution_scope 32 | && self.name == other.name 33 | && self.namespace == other.namespace 34 | } 35 | } 36 | 37 | impl ClassInfo { 38 | pub fn new( 39 | resolution_scope: ResolutionScope, 40 | namespace: &str, 41 | name: &str, 42 | fields: Vec, 43 | methods: Vec, 44 | parent: Option, 45 | ) -> Self { 46 | Self { 47 | resolution_scope, 48 | name: name.to_string(), 49 | namespace: namespace.to_string(), 50 | fields, 51 | methods, 52 | parent, 53 | method_table: vec![], 54 | } 55 | } 56 | 57 | pub fn new_ref( 58 | resolution_scope: ResolutionScope, 59 | namespace: &str, 60 | name: &str, 61 | fields: Vec, 62 | methods: Vec, 63 | parent: Option, 64 | ) -> ClassInfoRef { 65 | Rc::new(RefCell::new(Self { 66 | resolution_scope, 67 | name: name.to_string(), 68 | namespace: namespace.to_string(), 69 | fields, 70 | methods, 71 | parent, 72 | method_table: vec![], 73 | })) 74 | } 75 | 76 | pub fn new_ref_empty() -> ClassInfoRef { 77 | Rc::new(RefCell::new(Self { 78 | resolution_scope: ResolutionScope::None, 79 | name: "".to_string(), 80 | namespace: "".to_string(), 81 | fields: vec![], 82 | methods: vec![], 83 | parent: None, 84 | method_table: vec![], 85 | })) 86 | } 87 | 88 | pub fn get_method_index(&self, name: &str) -> Option { 89 | self.method_table 90 | .iter() 91 | .position(|m| m.borrow().get_name() == name) 92 | } 93 | 94 | pub fn get_field_index(&self, name: &str) -> Option { 95 | self.fields.iter().position(|f| f.name == name) 96 | } 97 | 98 | pub fn is_enum(&self) -> bool { 99 | match self.parent { 100 | Some(ref parent) => { 101 | let parent = parent.borrow(); 102 | (match parent.resolution_scope { 103 | ResolutionScope::AssemblyRef { ref name } if name == "mscorlib" => true, 104 | _ => false, 105 | }) && parent.namespace == "System" 106 | && parent.name == "Enum" 107 | } 108 | None => false, 109 | } 110 | } 111 | } 112 | 113 | impl ClassField { 114 | pub fn new(name: String, ty: Type) -> Self { 115 | ClassField { name, ty } 116 | } 117 | 118 | pub fn new_ty(ty: Type) -> Self { 119 | ClassField { 120 | name: "".to_string(), 121 | ty, 122 | } 123 | } 124 | } 125 | 126 | impl ResolutionScope { 127 | pub fn asm_ref(name: &str) -> Self { 128 | ResolutionScope::AssemblyRef { 129 | name: name.to_string(), 130 | } 131 | } 132 | 133 | pub fn get_name<'a>(&'a self) -> &'a str { 134 | match self { 135 | ResolutionScope::AssemblyRef { name } => name.as_str(), 136 | ResolutionScope::None => "", 137 | } 138 | } 139 | } 140 | 141 | impl fmt::Debug for ClassInfo { 142 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 143 | write!( 144 | f, 145 | "ClassInfo {{ resolution_scope: {:?}, name: {}, namespace: {}, fields: {:?}, methods: [omitted], parent: {:?}, vtable: [omitted] }}", 146 | self.resolution_scope, self.name, self.namespace, self.fields, self.parent 147 | ) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/metadata/header.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy)] 2 | #[repr(C, packed)] 3 | pub struct PEFileHeader { 4 | /// Always 0x14c 5 | pub machine: u16, 6 | 7 | /// Number of sections; indicates size of the Section Table, 8 | /// which immediately follows the headers. 9 | pub number_of_sections: u16, 10 | 11 | /// Time and date the file was created in seconds since 12 | /// January 1st 1970 00:00:00 or 0. 13 | pub time_date_stamp: u32, 14 | 15 | /// Always 0 16 | pub pointer_to_symbol_table: u32, 17 | 18 | /// Always 0 19 | pub number_of_symbols: u32, 20 | 21 | /// Size of the optional header, the format is described below. 22 | pub optional_header_size: u16, 23 | 24 | /// Flags indicating attributes of the file. 25 | pub characteristics: u16, 26 | } 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct PEOptionalHeader { 30 | /// Size of the code (text) section, or the sum of all code sections 31 | /// if there are multiple sections. 32 | pub code_size: u32, 33 | 34 | /// Size of the initialized data section, or the sum of all such 35 | /// sections if there are multiple data sections. 36 | pub initialized_data_size: u32, 37 | 38 | /// Size of the uninitialized data section, or the sum of all such 39 | /// sections if there are multiple unitinitalized data sections. 40 | pub uninitialized_data_size: u32, 41 | 42 | /// RVA of entry point , needs to point to bytes 0xFF 0x25 43 | /// followed by the RVA in a section marked execute/read for 44 | /// EXEs or 0 for DLLs 45 | pub entry_point_rva: u32, 46 | 47 | /// RVA of the code section. 48 | pub base_of_code: u32, 49 | 50 | /// RVA of the data section. 51 | pub base_of_data: u32, 52 | 53 | /// Shall be a multiple of 0x10000. 54 | pub image_base: u32, 55 | 56 | /// Shall be greater than File Alignment. 57 | pub section_alignment: u32, 58 | 59 | /// Size, in bytes, of image, including all headers and padding; 60 | /// shall be a multiple of Section Alignment. 61 | pub image_size: u32, 62 | 63 | /// Combined size of MS-DOS Header, PE Header, PE Optional 64 | /// Header and padding; shall be a multiple of the file alignment. 65 | pub header_size: u32, 66 | 67 | /// Subsystem required to run this image. 68 | pub sub_system: u16, 69 | 70 | /// Bits 0x100f shall be zero. 71 | pub dll_flags: u16, 72 | 73 | /// Should be 0x100000 (1Mb) 74 | pub stack_reserve_size: u32, 75 | 76 | /// Should be 0x1000 (4Kb) 77 | pub stack_commit_size: u32, 78 | 79 | /// Should be 0x100000 (1Mb) 80 | pub heap_reserve_size: u32, 81 | 82 | /// Should be 0x1000 (4Kb) 83 | pub heap_commit_size: u32, 84 | 85 | /// Shall be 0 86 | pub loader_flags: u32, 87 | 88 | /// Shall be 0x10 89 | pub number_of_data_directories: u32, 90 | 91 | // Data directories 92 | /// Import Table: RVA 93 | pub import_table_rva: u32, 94 | 95 | /// Import Table: Size of Import Table 96 | pub import_table_size: u32, 97 | 98 | /// Base Relocation Table: RVA 99 | pub base_relocation_table_rva: u32, 100 | 101 | /// Base Relocation Table: Block size 102 | pub base_relocation_table_size: u32, 103 | 104 | /// IAT: RVA 105 | pub iat_rva: u32, 106 | 107 | /// IAT: Size 108 | pub iat_size: u32, 109 | 110 | /// CLI Header: RVA 111 | pub cli_header_rva: u32, 112 | 113 | /// CLI Header: Size 114 | pub cli_header_size: u32, 115 | } 116 | 117 | #[derive(Debug, Clone)] 118 | pub struct SectionHeader { 119 | /// An 8-byte, null-padded ASCII string. There is no terminating null 120 | /// if the string is exactly eight characters long. 121 | pub name: String, 122 | 123 | /// Total size of the section in bytes. If this value is greater than 124 | /// SizeOfRawData, the section is zero-padded. 125 | pub virtual_size: u32, 126 | 127 | /// For executable images this is the address of the first byte of the 128 | /// section, when loaded into memory, relative to the image base. 129 | pub virtual_address: u32, 130 | 131 | /// Size of the initialized data on disk in bytes, shall be a multiple of 132 | /// FileAlignment from the PE header. If this is less than VirtualSize 133 | /// the remainder of the section is zero filled. Because this field is 134 | /// rounded while the VirtualSize field is not it is possible for this to 135 | /// be greater than VirtualSize as well. When a section contains only 136 | /// uninitialized data, this field should be 0. 137 | pub size_of_raw_data: u32, 138 | 139 | /// Offset of section’s first page within the PE file. This shall be a 140 | /// multiple of FileAlignment from the optional header. When a 141 | /// section contains only uninitialized data, this field should be 0. 142 | pub pointer_to_raw_data: u32, 143 | 144 | /// Should be 0 145 | pub pointer_to_relocations: u32, 146 | 147 | /// Should be 0 148 | pub pointer_to_linenumbers: u32, 149 | 150 | /// Should be 0 151 | pub number_of_relocations: u16, 152 | 153 | /// Should be 0 154 | pub number_of_linenumbers: u16, 155 | 156 | /// Flags describing section’s characteristics 157 | pub characteristics: u32, 158 | } 159 | 160 | #[derive(Debug, Clone)] 161 | pub struct CLIHeader { 162 | /// Size of the header in bytes 163 | pub cb: u32, 164 | 165 | /// The minimum version of the runtime required to run 166 | /// this program, currently 2. 167 | pub major_runtime_version: u16, 168 | 169 | /// The minor portion of the version, currently 0. 170 | pub minor_runtime_version: u16, 171 | 172 | /// Metadata: RVA 173 | pub metadata_rva: u32, 174 | 175 | /// Metadata: Size 176 | pub metadata_size: u32, 177 | 178 | /// Flags describing this runtime image. 179 | pub flags: u32, 180 | 181 | /// Token for the MethodDef or File of the entry point 182 | /// for the image 183 | pub entry_point_token: u32, 184 | 185 | /// Resources: RVA 186 | pub resources_rva: u32, 187 | 188 | /// Resources: Size 189 | pub resources_size: u32, 190 | 191 | /// RVA of the hash data for this PE file used by the 192 | /// CLI loader for binding 193 | pub strong_name_signature_rva: u32, 194 | 195 | /// Versioning of the hash data for this PE file used by the 196 | /// CLI loader for binding 197 | pub strong_name_signature_version: u32, 198 | 199 | pub vtable_fixups_virtual_address: u32, 200 | 201 | pub vtable_fixups_size: u16, 202 | 203 | pub vtable_fixups_type: u16, 204 | } 205 | 206 | #[derive(Debug, Clone)] 207 | pub struct MetaDataHeader { 208 | /// Version string 209 | pub version: String, 210 | 211 | /// Number of streams 212 | pub streams: u16, 213 | } 214 | 215 | #[derive(Debug, Clone)] 216 | pub struct StreamHeader { 217 | /// Memory offset to start of this stream from start of the 218 | /// metadata root 219 | pub offset: u32, 220 | 221 | /// Size of this stream in bytes, shall be a multiple of 4. 222 | pub size: u32, 223 | 224 | /// Name of the stream 225 | pub name: String, 226 | } 227 | 228 | #[allow(dead_code)] 229 | mod pe_file_header_characteristics { 230 | pub const IMAGE_FILE_RELOCS_STRIPPED: u16 = 0x0001; 231 | pub const IMAGE_FILE_EXECUTABLE_IMAGE: u16 = 0x0002; 232 | pub const IMAGE_FILE_32BIT_MACHINE: u16 = 0x0100; 233 | pub const IMAGE_FILE_DLL: u16 = 0x2000; 234 | } 235 | 236 | #[allow(dead_code)] 237 | mod sub_system { 238 | pub const IMAGE_SUBSYSTEM_WINDOWS_CUI: u16 = 0x3; 239 | pub const IMAGE_SUBSYSTEM_WINDOWS_GUI: u16 = 0x2; 240 | } 241 | 242 | #[allow(dead_code)] 243 | mod section_characteristics { 244 | /// Section contains code. 245 | pub const IMAGE_SCN_CNT_CODE: u32 = 0x0000_0020; 246 | 247 | /// Section contains initialized data. 248 | pub const IMAGE_SCN_CNT_INITIALIZED_DATA: u32 = 0x0000_0040; 249 | 250 | /// Section contains uninitialized data. 251 | pub const IMAGE_SCN_CNT_UNINITIALIZED_DATA: u32 = 0x0000_0080; 252 | 253 | /// Section can be executed as code. 254 | pub const IMAGE_SCN_MEM_EXECUTE: u32 = 0x2000_0000; 255 | 256 | /// Section can be read. 257 | pub const IMAGE_SCN_MEM_READ: u32 = 0x4000_0000; 258 | 259 | /// Section can be written to. 260 | pub const IMAGE_SCN_MEM_WRITE: u32 = 0x8000_0000; 261 | } 262 | -------------------------------------------------------------------------------- /src/metadata/image.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | metadata::{ 3 | assembly::*, class::*, metadata::*, method::*, pe_parser::*, signature::*, token::*, 4 | }, 5 | util::{name_path::*, resolver::*}, 6 | }; 7 | use rustc_hash::FxHashMap; 8 | use std::path; 9 | use std::{cell::RefCell, rc::Rc}; 10 | 11 | pub type RVA = u32; 12 | 13 | #[derive(Debug, Clone)] 14 | pub struct Image { 15 | /// CLI Info 16 | pub cli_info: CLIInfo, 17 | 18 | /// Metadata streams 19 | pub metadata: MetaDataStreams, 20 | 21 | /// PE file reader 22 | pub pe_parser: Option>>, 23 | 24 | /// Cache ``MethodInfoRef`` by RVA 25 | pub method_cache: FxHashMap, 26 | 27 | /// Cache ``ClassInfoRef`` by token 28 | pub class_cache: FxHashMap, 29 | 30 | /// Assembly References 31 | pub asm_refs: FxHashMap, 32 | 33 | /// File name from which this image is loaded 34 | pub filename: path::PathBuf, 35 | } 36 | 37 | impl Image { 38 | pub fn new( 39 | cli_info: CLIInfo, 40 | metadata: MetaDataStreams, 41 | filename: path::PathBuf, 42 | pe_parser: Option>>, 43 | ) -> Self { 44 | Self { 45 | cli_info, 46 | metadata, 47 | pe_parser, 48 | method_cache: FxHashMap::default(), 49 | class_cache: FxHashMap::default(), 50 | asm_refs: FxHashMap::default(), 51 | filename, 52 | } 53 | } 54 | 55 | pub fn collect_all_reachable_assemblies(&self, asms: &mut FxHashMap) { 56 | for (name, asm) in &self.asm_refs { 57 | if asms.contains_key(name) { 58 | continue; 59 | } 60 | asms.insert(name.clone(), asm.clone()); 61 | asm.borrow().image.collect_all_reachable_assemblies(asms); 62 | } 63 | } 64 | 65 | pub fn setup_all_asmref(&mut self, loaded: &mut FxHashMap) { 66 | for asmref_ in self.metadata.get_table(TableKind::AssemblyRef) { 67 | let asmref = retrieve!(asmref_, Table::AssemblyRef); 68 | let name = self.get_string(asmref.name); 69 | 70 | // TODO: Treat as special 71 | if name == "mscorlib" { 72 | continue; 73 | } 74 | 75 | // Already loaded 76 | if let Some(asm) = loaded.get(name) { 77 | self.asm_refs.insert(name.to_string(), asm.clone()); 78 | continue; 79 | } 80 | 81 | let asm = Assembly::load_exclusive( 82 | { 83 | let mut path = self.filename.parent().unwrap().to_path_buf(); 84 | path.push(name.clone()); 85 | path.set_extension("dll"); 86 | path 87 | }, 88 | loaded, 89 | ) 90 | .unwrap(); 91 | 92 | loaded.insert(name.to_string(), asm.clone()); 93 | 94 | self.asm_refs.insert(name.to_string(), asm); 95 | } 96 | } 97 | 98 | pub fn setup_all_typeref(&mut self) { 99 | for (i, typeref) in self 100 | .metadata 101 | .get_table(TableKind::TypeRef) 102 | .iter() 103 | .enumerate() 104 | { 105 | let token = encode_token(TableKind::TypeRef.into(), i as u32 + 1); 106 | let tref = retrieve!(typeref, Table::TypeRef); 107 | let namespace = self.get_string(tref.type_namespace); 108 | let name = self.get_string(tref.type_name); 109 | let asm = retrieve!( 110 | self.metadata 111 | .get_table_entry(tref.resolution_scope_decoded()) 112 | .unwrap(), 113 | Table::AssemblyRef 114 | ); 115 | let asm_name = self.get_string(asm.name); 116 | 117 | // TODO: Treat as special 118 | if asm_name == "mscorlib" { 119 | if let Some(class) = get_mscorlib().get(TypePath(vec!["mscorlib", namespace, name])) 120 | { 121 | self.class_cache.insert(token, class.clone()); 122 | } 123 | continue; 124 | } 125 | 126 | let class = self 127 | .asm_refs 128 | .get(asm_name) 129 | .unwrap() 130 | .borrow() 131 | .image 132 | .find_class(TypePath(vec![asm_name, namespace, name])) 133 | .unwrap(); 134 | 135 | self.class_cache.insert(token, class.clone()); 136 | } 137 | } 138 | 139 | pub fn setup_all_class(&mut self) { 140 | let typedefs = self.metadata.get_table(TableKind::TypeDef); 141 | let fields = self.metadata.get_table(TableKind::Field); 142 | let methoddefs = self.metadata.get_table(TableKind::MethodDef); 143 | 144 | for (i, typedef) in typedefs.iter().enumerate() { 145 | let typedef = retrieve!(typedef, Table::TypeDef); 146 | let next_typedef = typedefs.get(i + 1); 147 | let (field_range, method_range) = { 148 | let field_start = typedef.field_list as usize - 1; 149 | let method_start = typedef.method_list as usize - 1; 150 | next_typedef.map_or( 151 | (field_start..fields.len(), method_start..methoddefs.len()), 152 | |table| { 153 | let td = retrieve!(table, Table::TypeDef); 154 | ( 155 | field_start..(td.field_list as usize - 1), 156 | method_start..(td.method_list as usize - 1), 157 | ) 158 | }, 159 | ) 160 | }; 161 | 162 | let class = self 163 | .get_class(encode_token(TableKind::TypeDef.into(), i as u32 + 1)) 164 | .unwrap() 165 | .clone(); 166 | 167 | // Set class fields 168 | class.borrow_mut().fields = self.metadata.get_table(TableKind::Field)[field_range] 169 | .iter() 170 | .map(|t| { 171 | let ft = retrieve!(t, Table::Field); 172 | let name = self.get_string(ft.name).to_string(); 173 | let mut sig = self.get_blob(ft.signature).iter(); 174 | assert_eq!(sig.next().unwrap(), &0x06); 175 | let ty = Type::into_type(self, &mut sig).unwrap(); 176 | ClassField { name, ty } 177 | }) 178 | .collect(); 179 | 180 | let pe_parser_ref = self.pe_parser.as_ref().unwrap(); 181 | let mut pe_parser = pe_parser_ref.borrow_mut(); 182 | 183 | // Set class methods 184 | let mut methods = vec![]; 185 | for mdef in &self.metadata.get_table(TableKind::MethodDef)[method_range] { 186 | let mdef = retrieve!(mdef, Table::MethodDef); 187 | let method = pe_parser.read_method(self, &class, mdef.rva).unwrap(); 188 | self.method_cache.insert(mdef.rva, method.clone()); 189 | methods.push(method) 190 | } 191 | class.borrow_mut().methods = methods; 192 | 193 | // Set parent class 194 | if typedef.extends != 0 { 195 | let token = decode_typedef_or_ref_token(typedef.extends as u32); 196 | let typedef_or_ref = self.metadata.get_table_entry(token).unwrap(); 197 | match typedef_or_ref { 198 | Table::TypeDef(_) => { 199 | class.borrow_mut().parent = Some(self.get_class(token).unwrap().clone()); 200 | } 201 | Table::TypeRef(_) => { 202 | class.borrow_mut().parent = Some(self.get_class(token).unwrap().clone()); 203 | } 204 | _ => unreachable!(), 205 | } 206 | } 207 | } 208 | 209 | self.setup_all_class_method_table(); 210 | } 211 | 212 | pub fn define_all_class(&mut self) { 213 | for (i, typedef) in self 214 | .metadata 215 | .get_table(TableKind::TypeDef) 216 | .iter() 217 | .enumerate() 218 | { 219 | let typedef = retrieve!(typedef, Table::TypeDef); 220 | let class_info = ClassInfo::new_ref( 221 | ResolutionScope::AssemblyRef { 222 | name: self.get_assembly_name().unwrap().to_string(), 223 | }, 224 | self.get_string(typedef.type_namespace), 225 | self.get_string(typedef.type_name), 226 | vec![], 227 | vec![], 228 | None, 229 | ); 230 | self.class_cache.insert( 231 | encode_token(TableKind::TypeDef.into(), i as u32 + 1), 232 | class_info.clone(), 233 | ); 234 | } 235 | } 236 | 237 | fn get_assembly_name(&self) -> Option<&str> { 238 | let asm = retrieve!( 239 | self.metadata.get_table(TableKind::Assembly).get(0)?, 240 | Table::Assembly 241 | ); 242 | Some(self.get_string(asm.name)) 243 | } 244 | 245 | fn setup_all_class_method_table(&mut self) { 246 | for (_token, class_ref) in &self.class_cache { 247 | self.construct_class_method_table(class_ref); 248 | } 249 | } 250 | 251 | fn construct_class_method_table(&self, class_ref: &ClassInfoRef) { 252 | let mut class = class_ref.borrow_mut(); 253 | let mut method_table = match &class.parent { 254 | Some(parent) => { 255 | self.construct_class_method_table(parent); 256 | parent.borrow().method_table.clone() 257 | } 258 | // If already borrowed, it means that ``class`` is System::Object. 259 | None => mscorlib_system_object() 260 | .try_borrow() 261 | .map(|sys_obj| sys_obj.methods.clone()) 262 | .unwrap_or_else(|_| class.methods.clone()), 263 | }; 264 | 265 | for minforef in &class.methods { 266 | let minfo = minforef.borrow(); 267 | 268 | if minfo.is_static() { 269 | continue; 270 | } 271 | 272 | if let Some(m) = method_table 273 | .iter_mut() 274 | .find(|m| m.borrow().get_name() == minfo.get_name()) 275 | { 276 | // Override 277 | *m = minforef.clone() 278 | } else { 279 | // New slot 280 | method_table.push(minforef.clone()); 281 | } 282 | } 283 | 284 | class.method_table = method_table; 285 | } 286 | 287 | pub fn get_class>(&self, token: T) -> Option<&ClassInfoRef> { 288 | self.class_cache.get(&token.into()) 289 | } 290 | 291 | pub fn get_string>(&self, n: T) -> &str { 292 | self.metadata.strings.get(&n.into()).unwrap().as_str() 293 | } 294 | 295 | pub fn get_user_string>(&self, n: T) -> &Vec { 296 | self.metadata.user_strings.get(&n.into()).unwrap() 297 | } 298 | 299 | pub fn get_entry_method(&mut self) -> Option { 300 | let method_or_file = self 301 | .metadata 302 | .get_table_entry(self.cli_info.cli_header.entry_point_token)?; 303 | let mdef = match method_or_file { 304 | Table::MethodDef(t) => t, 305 | // TOOD: File 306 | _ => return None, 307 | }; 308 | self.get_method_by_rva(mdef.rva) 309 | } 310 | 311 | pub fn get_method_by_rva(&self, rva: u32) -> Option { 312 | self.method_cache.get(&rva).map(|m| m.clone()) 313 | } 314 | 315 | pub fn get_method_def_table_by_rva(&self, rva: u32) -> Option<&MethodDefTable> { 316 | for method_def in self.metadata.get_table(TableKind::MethodDef) { 317 | match method_def { 318 | Table::MethodDef(mdt) if mdt.rva == rva => return Some(mdt), 319 | Table::MethodDef(_) => continue, 320 | _ => return None, 321 | } 322 | } 323 | None 324 | } 325 | 326 | pub fn get_blob>(&self, n: T) -> &Vec { 327 | self.metadata.blob.get(&n.into()).unwrap() 328 | } 329 | 330 | pub fn get_path_from_type_ref_table<'a>( 331 | &'a self, 332 | type_ref_table: &TypeRefTable, 333 | ) -> TypePath<'a> { 334 | let token = type_ref_table.resolution_scope_decoded(); 335 | let assembly_ref_table = retrieve!( 336 | self.metadata.get_table_entry(token).unwrap(), 337 | Table::AssemblyRef 338 | ); 339 | let asm_ref_name = self.get_string(assembly_ref_table.name); 340 | let ty_namespace = self.get_string(type_ref_table.type_namespace); 341 | let ty_name = self.get_string(type_ref_table.type_name); 342 | TypePath(vec![asm_ref_name, ty_namespace, ty_name]) 343 | } 344 | 345 | pub fn get_method_ref_type_from_signature(&self, signature: u16) -> Type { 346 | let sig = self.get_blob(signature); 347 | SignatureParser::new(sig) 348 | .parse_method_ref_sig(self) 349 | .unwrap() 350 | } 351 | 352 | pub fn find_class<'a, P: Into>>(&self, path_: P) -> Option { 353 | let path = path_.into(); 354 | for info in self.class_cache.values() { 355 | if (&*info.borrow()).into(): TypePath == path { 356 | return Some(info.clone()); 357 | } 358 | } 359 | None 360 | } 361 | } 362 | 363 | thread_local! { 364 | pub static MSCORLIB: Rc> = { 365 | #[rustfmt::skip] 366 | macro_rules! parse_ty { 367 | (void) => { Type::void_ty() }; 368 | (i4 ) => { Type::i4_ty() }; 369 | (r8 ) => { Type::r8_ty() }; 370 | (char) => { Type::char_ty() }; 371 | (obj ) => { Type::object_ty() }; 372 | (str ) => { Type::string_ty() }; 373 | } 374 | 375 | macro_rules! method { 376 | ($ret_ty:ident, [ $($param_ty:ident),* ], $f:expr, $name:expr) => {{ 377 | method!([0], $ret_ty, [$($param_ty),*], $f, $name) 378 | }}; 379 | ([$flags:expr], $ret_ty:ident, [ $($param_ty:ident),* ], $name:expr, $class:expr) => {{ 380 | Rc::new(RefCell::new(MethodInfo::MRef(MemberRefInfo { 381 | name: $name.to_string(), class: $class.clone(), 382 | ty: Type::full_method_ty($flags, parse_ty!($ret_ty), &[$(parse_ty!($param_ty)),*]), 383 | }))) 384 | }} 385 | } 386 | 387 | macro_rules! class { ($name:ident, $parent:expr) => {{ 388 | ClassInfo::new_ref( 389 | ResolutionScope::asm_ref("mscorlib"), 390 | "System", stringify!($name), vec![], vec![], $parent, 391 | )}}} 392 | 393 | let class_system_obj_ref = class!(Object, None); 394 | let class_system_int32_ref = class!(Int32, Some(class_system_obj_ref.clone())); 395 | let class_system_string_ref = class!(String, Some(class_system_obj_ref.clone())); 396 | let class_system_valuetype_ref = class!(ValueType, Some(class_system_obj_ref.clone())); 397 | let class_system_enum_ref = class!(Enum, Some(class_system_valuetype_ref.clone())); 398 | 399 | { 400 | let mut class_system_obj = class_system_obj_ref.borrow_mut(); 401 | let mut class_system_int32 = class_system_int32_ref.borrow_mut(); 402 | let mut class_system_string = class_system_string_ref.borrow_mut(); 403 | let mut class_system_valuetype = class_system_valuetype_ref.borrow_mut(); 404 | let mut class_system_enum = class_system_enum_ref.borrow_mut(); 405 | 406 | class_system_obj.methods = 407 | vec![method!([0x20], str, [], "ToString", class_system_obj_ref)]; 408 | class_system_int32.methods = 409 | vec![method!([0x20], str, [], "ToString", class_system_int32_ref)]; 410 | class_system_string.methods = vec![ 411 | method!([0x20], str, [], "ToString", class_system_string_ref), 412 | method!([0x20], char, [i4], "get_Chars", class_system_string_ref), 413 | method!([0x20], i4, [], "get_Length", class_system_string_ref), 414 | ]; 415 | class_system_valuetype.methods = 416 | vec![method!([0x20], str, [], "ToString", class_system_valuetype_ref)]; 417 | class_system_enum.methods = 418 | vec![method!([0x20], str, [], "ToString", class_system_enum_ref)]; 419 | 420 | class_system_obj.method_table = class_system_obj.methods.clone(); 421 | class_system_int32.method_table = class_system_int32.methods.clone(); 422 | class_system_string.method_table = class_system_string.methods.clone(); 423 | class_system_valuetype.method_table = class_system_valuetype.methods.clone(); 424 | class_system_enum.method_table = class_system_enum.methods.clone(); 425 | 426 | // class_system_obj.fields = vec![]; 427 | class_system_int32.fields = vec![ClassField::new_ty(Type::i4_ty())]; 428 | class_system_string.fields = vec![ClassField::new_ty(Type::ptr_ty(Type::char_ty()))]; 429 | } 430 | 431 | let mut resolver = NameResolver::new(); 432 | 433 | resolver.add( 434 | TypePath(vec!["mscorlib", "System", "Object"]), 435 | class_system_obj_ref, 436 | ); 437 | resolver.add( 438 | TypePath(vec!["mscorlib", "System", "Int32"]), 439 | class_system_int32_ref, 440 | ); 441 | resolver.add( 442 | TypePath(vec!["mscorlib", "System", "String"]), 443 | class_system_string_ref, 444 | ); 445 | resolver.add( 446 | TypePath(vec!["mscorlib", "System", "ValueType"]), 447 | class_system_valuetype_ref, 448 | ); 449 | resolver.add( 450 | TypePath(vec!["mscorlib", "System", "Enum"]), 451 | class_system_enum_ref, 452 | ); 453 | 454 | Rc::new(resolver) 455 | }; 456 | } 457 | 458 | pub fn mscorlib_system_string() -> ClassInfoRef { 459 | get_mscorlib() 460 | .get(TypePath(vec!["mscorlib", "System", "String"])) 461 | .unwrap() 462 | .clone() 463 | } 464 | 465 | pub fn mscorlib_system_int32() -> ClassInfoRef { 466 | get_mscorlib() 467 | .get(TypePath(vec!["mscorlib", "System", "Int32"])) 468 | .unwrap() 469 | .clone() 470 | } 471 | 472 | pub fn mscorlib_system_object() -> ClassInfoRef { 473 | get_mscorlib() 474 | .get(TypePath(vec!["mscorlib", "System", "Object"])) 475 | .unwrap() 476 | .clone() 477 | } 478 | 479 | pub fn get_mscorlib() -> Rc> { 480 | MSCORLIB.with(|mscorlib| mscorlib.clone()) 481 | } 482 | -------------------------------------------------------------------------------- /src/metadata/metadata.rs: -------------------------------------------------------------------------------- 1 | use crate::metadata::{ 2 | header::{CLIHeader, SectionHeader}, 3 | token::*, 4 | }; 5 | use rustc_hash::FxHashMap; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct CLIInfo { 9 | pub cli_header: CLIHeader, 10 | pub sections: Vec, 11 | } 12 | 13 | /// #~ Stream 14 | #[derive(Debug, Clone)] 15 | pub struct MetaDataStream { 16 | /// Major version of table schemata; shall be 2 17 | pub major_version: u8, 18 | 19 | /// Minor version of table schemata; shall be 0 20 | pub minor_version: u8, 21 | 22 | /// Bit vector for heap sizes. 23 | pub heap_sizes: u8, 24 | 25 | /// Bit vector of present tables, let n be the number of bits that 26 | /// are 1. 27 | pub valid: u64, 28 | 29 | /// Bit vector of sorted tables. 30 | pub sorted: u64, 31 | 32 | /// Array of n 4-byte unsigned integers indicating the number of 33 | /// rows for each present table. 34 | pub rows: Vec, 35 | 36 | /// The sequence of physical tables. 37 | pub tables: Vec>, 38 | } 39 | 40 | #[derive(Debug, Clone)] 41 | pub struct MetaDataStreams { 42 | pub metadata_stream: MetaDataStream, 43 | pub strings: FxHashMap, 44 | pub user_strings: FxHashMap>, 45 | pub blob: FxHashMap>, 46 | pub guid: String, 47 | } 48 | 49 | impl MetaDataStreams { 50 | pub fn get_table>(&self, table_kind: T) -> &Vec { 51 | &self.metadata_stream.tables[table_kind.into()] 52 | } 53 | 54 | pub fn get_table_entry>(&self, token: T) -> Option
{ 55 | let DecodedToken(table, entry) = decode_token(token.into()); 56 | 57 | if entry == 0 { 58 | return None; 59 | } 60 | 61 | self.get_table(table as usize) 62 | .get(entry as usize - 1) 63 | .map(|t| *t) 64 | } 65 | } 66 | 67 | pub const NUM_TABLES: usize = 45; 68 | 69 | #[derive(Debug, Clone, PartialEq, Copy)] 70 | pub enum TableKind { 71 | Assembly, 72 | AssemblyOS, 73 | AssemblyProcessor, 74 | AssemblyRef, 75 | AssemblyRefOS, 76 | AssemblyRefProcessor, 77 | ClassLayout, 78 | Constant, 79 | CustomAttribute, 80 | DeclSecurity, 81 | EventMap, 82 | Event, 83 | ExportedType, 84 | Field, 85 | FieldLayout, 86 | FieldMarshal, 87 | FieldRVA, 88 | File, 89 | GenericParam, 90 | GenericParamConstraint, 91 | ImplMap, 92 | InterfaceImpl, 93 | ManifestResource, 94 | MemberRef, 95 | MethodDef, 96 | MethodImpl, 97 | MethodSemantics, 98 | MethodSpec, 99 | Module, 100 | ModuleRef, 101 | NestedClass, 102 | Param, 103 | Property, 104 | PropertyMap, 105 | StandAloneSig, 106 | TypeDef, 107 | TypeRef, 108 | TypeSpec, 109 | } 110 | 111 | #[derive(Debug, Clone, Copy)] 112 | pub enum Table { 113 | Assembly(AssemblyTable), 114 | // AssemblyOS, 115 | // AssemblyProcessor, 116 | AssemblyRef(AssemblyRefTable), 117 | // AssemblyRefOS, 118 | // AssemblyRefProcessor, 119 | // ClassLayout, 120 | Constant(ConstantTable), 121 | CustomAttribute(CustomAttributeTable), 122 | // DeclSecurity, 123 | // EventMap, 124 | // Event, 125 | // ExportedType, 126 | Field(FieldTable), 127 | // FieldLayout, 128 | // FieldMarshal, 129 | // FieldRVA, 130 | // File, 131 | // GenericParam, 132 | // GenericParamConstraint, 133 | // ImplMap, 134 | // InterfaceImpl, 135 | // ManifestResource, 136 | MemberRef(MemberRefTable), 137 | MethodDef(MethodDefTable), 138 | // MethodImpl, 139 | // MethodSemantics, 140 | // MethodSpec, 141 | Module(ModuleTable), 142 | // ModuleRef, 143 | // NestedClass, 144 | Param(ParamTable), 145 | // Property, 146 | // PropertyMap, 147 | StandAloneSig(StandAlongSigTable), 148 | TypeDef(TypeDefTable), 149 | TypeRef(TypeRefTable), 150 | // TypeSpec, 151 | } 152 | 153 | /// II.22.2 Assembly 154 | #[derive(Debug, Clone, PartialEq, Copy)] 155 | #[repr(C, packed)] 156 | pub struct AssemblyTable { 157 | pub hash_alg_id: u32, 158 | pub major_version: u16, 159 | pub minor_version: u16, 160 | pub build_number: u16, 161 | pub revision_number: u16, 162 | pub flags: u32, 163 | pub public_key: u16, 164 | pub name: u16, 165 | pub culture: u16, 166 | } 167 | 168 | /// II.22.5 AssemblyRef 169 | #[derive(Debug, Clone, PartialEq, Copy)] 170 | #[repr(C, packed)] 171 | pub struct AssemblyRefTable { 172 | pub major_version: u16, 173 | pub minor_version: u16, 174 | pub build_number: u16, 175 | pub revision_number: u16, 176 | pub flags: u32, 177 | pub public_key_or_token: u16, 178 | pub name: u16, 179 | pub culture: u16, 180 | pub hash_value: u16, 181 | } 182 | 183 | /// II.22.9 Constant 184 | #[derive(Debug, Clone, PartialEq, Copy)] 185 | #[repr(C, packed)] 186 | pub struct ConstantTable { 187 | pub type_: u16, 188 | pub parent: u16, 189 | pub value: u16, 190 | } 191 | 192 | /// II.22.10 CustomAttribute 193 | #[derive(Debug, Clone, PartialEq, Copy)] 194 | #[repr(C, packed)] 195 | pub struct CustomAttributeTable { 196 | pub parent: u16, 197 | pub type_: u16, 198 | pub value: u16, 199 | } 200 | 201 | /// II.22.15 Field 202 | #[derive(Debug, Clone, PartialEq, Copy)] 203 | #[repr(C, packed)] 204 | pub struct FieldTable { 205 | pub flags: u16, 206 | pub name: u16, 207 | pub signature: u16, 208 | } 209 | 210 | /// II.22.25 MemberRef 211 | #[derive(Debug, Clone, PartialEq, Copy)] 212 | #[repr(C, packed)] 213 | pub struct MemberRefTable { 214 | pub class: u16, 215 | pub name: u16, 216 | pub signature: u16, 217 | } 218 | 219 | /// II.22.26 MethodDef 220 | #[derive(Debug, Clone, PartialEq, Copy)] 221 | #[repr(C, packed)] 222 | pub struct MethodDefTable { 223 | pub rva: u32, 224 | pub impl_flags: u16, 225 | pub flags: u16, 226 | pub name: u16, 227 | pub signature: u16, 228 | pub param_list: u16, 229 | } 230 | 231 | /// II.22.30 Module 232 | #[derive(Debug, Clone, PartialEq, Copy)] 233 | #[repr(C, packed)] 234 | pub struct ModuleTable { 235 | pub generation: u16, 236 | pub name: u16, 237 | pub mvid: u16, 238 | pub env_id: u16, 239 | pub env_base_id: u16, 240 | } 241 | 242 | /// II.22.33 Param 243 | #[derive(Debug, Clone, PartialEq, Copy)] 244 | #[repr(C, packed)] 245 | pub struct ParamTable { 246 | flags: u16, 247 | sequence: u16, 248 | name: u16, 249 | } 250 | 251 | /// II.22.36 StandAloneSig 252 | #[derive(Debug, Clone, PartialEq, Copy)] 253 | #[repr(C, packed)] 254 | pub struct StandAlongSigTable { 255 | pub signature: u16, 256 | } 257 | 258 | /// II.22.37 TypeDef 259 | #[derive(Debug, Clone, PartialEq, Copy)] 260 | #[repr(C, packed)] 261 | pub struct TypeDefTable { 262 | pub flags: u32, 263 | pub type_name: u16, 264 | pub type_namespace: u16, 265 | pub extends: u16, 266 | pub field_list: u16, 267 | pub method_list: u16, 268 | } 269 | 270 | /// II.22.38 TypeRef 271 | #[derive(Debug, Clone, PartialEq, Copy)] 272 | #[repr(C, packed)] 273 | pub struct TypeRefTable { 274 | pub resolution_scope: u16, 275 | pub type_name: u16, 276 | pub type_namespace: u16, 277 | } 278 | 279 | impl MemberRefTable { 280 | pub fn class_decoded(&self) -> DecodedToken { 281 | decode_member_ref_parent_token(self.class) 282 | } 283 | 284 | pub fn class2token(&self) -> Token { 285 | self.class_decoded().into() 286 | } 287 | } 288 | 289 | impl TypeRefTable { 290 | pub fn resolution_scope_decoded(&self) -> DecodedToken { 291 | decode_resolution_scope_token(self.resolution_scope) 292 | } 293 | } 294 | 295 | impl TableKind { 296 | pub fn table_kinds(valid: u64) -> Vec { 297 | let mut tables = vec![]; 298 | for i in 0..64 { 299 | if valid & (1 << i) > 0 { 300 | tables.push(TableKind::into_table_kind(i).unwrap()) 301 | } 302 | } 303 | tables 304 | } 305 | 306 | pub fn into_table_kind>(n: I) -> Option { 307 | match n.into() { 308 | 0x20 => Some(TableKind::Assembly), 309 | 0x22 => Some(TableKind::AssemblyOS), 310 | 0x21 => Some(TableKind::AssemblyProcessor), 311 | 0x23 => Some(TableKind::AssemblyRef), 312 | 0x25 => Some(TableKind::AssemblyRefOS), 313 | 0x24 => Some(TableKind::AssemblyRefProcessor), 314 | 0x0F => Some(TableKind::ClassLayout), 315 | 0x0B => Some(TableKind::Constant), 316 | 0x0C => Some(TableKind::CustomAttribute), 317 | 0x0E => Some(TableKind::DeclSecurity), 318 | 0x12 => Some(TableKind::EventMap), 319 | 0x14 => Some(TableKind::Event), 320 | 0x27 => Some(TableKind::ExportedType), 321 | 0x04 => Some(TableKind::Field), 322 | 0x10 => Some(TableKind::FieldLayout), 323 | 0x0D => Some(TableKind::FieldMarshal), 324 | 0x1D => Some(TableKind::FieldRVA), 325 | 0x26 => Some(TableKind::File), 326 | 0x2A => Some(TableKind::GenericParam), 327 | 0x2C => Some(TableKind::GenericParamConstraint), 328 | 0x1C => Some(TableKind::ImplMap), 329 | 0x09 => Some(TableKind::InterfaceImpl), 330 | 0x28 => Some(TableKind::ManifestResource), 331 | 0x0A => Some(TableKind::MemberRef), 332 | 0x06 => Some(TableKind::MethodDef), 333 | 0x19 => Some(TableKind::MethodImpl), 334 | 0x18 => Some(TableKind::MethodSemantics), 335 | 0x2B => Some(TableKind::MethodSpec), 336 | 0x00 => Some(TableKind::Module), 337 | 0x1A => Some(TableKind::ModuleRef), 338 | 0x29 => Some(TableKind::NestedClass), 339 | 0x08 => Some(TableKind::Param), 340 | 0x17 => Some(TableKind::Property), 341 | 0x15 => Some(TableKind::PropertyMap), 342 | 0x11 => Some(TableKind::StandAloneSig), 343 | 0x02 => Some(TableKind::TypeDef), 344 | 0x01 => Some(TableKind::TypeRef), 345 | 0x1B => Some(TableKind::TypeSpec), 346 | _ => None, 347 | } 348 | } 349 | 350 | pub fn into_num(self) -> usize { 351 | match self { 352 | TableKind::Assembly => 0x20, 353 | TableKind::AssemblyOS => 0x22, 354 | TableKind::AssemblyProcessor => 0x21, 355 | TableKind::AssemblyRef => 0x23, 356 | TableKind::AssemblyRefOS => 0x25, 357 | TableKind::AssemblyRefProcessor => 0x24, 358 | TableKind::ClassLayout => 0x0F, 359 | TableKind::Constant => 0x0B, 360 | TableKind::CustomAttribute => 0x0C, 361 | TableKind::DeclSecurity => 0x0E, 362 | TableKind::EventMap => 0x12, 363 | TableKind::Event => 0x14, 364 | TableKind::ExportedType => 0x27, 365 | TableKind::Field => 0x04, 366 | TableKind::FieldLayout => 0x10, 367 | TableKind::FieldMarshal => 0x0D, 368 | TableKind::FieldRVA => 0x1D, 369 | TableKind::File => 0x26, 370 | TableKind::GenericParam => 0x2A, 371 | TableKind::GenericParamConstraint => 0x2C, 372 | TableKind::ImplMap => 0x1C, 373 | TableKind::InterfaceImpl => 0x09, 374 | TableKind::ManifestResource => 0x28, 375 | TableKind::MemberRef => 0x0A, 376 | TableKind::MethodDef => 0x06, 377 | TableKind::MethodImpl => 0x19, 378 | TableKind::MethodSemantics => 0x18, 379 | TableKind::MethodSpec => 0x2B, 380 | TableKind::Module => 0x00, 381 | TableKind::ModuleRef => 0x1A, 382 | TableKind::NestedClass => 0x29, 383 | TableKind::Param => 0x08, 384 | TableKind::Property => 0x17, 385 | TableKind::PropertyMap => 0x15, 386 | TableKind::StandAloneSig => 0x11, 387 | TableKind::TypeDef => 0x02, 388 | TableKind::TypeRef => 0x01, 389 | TableKind::TypeSpec => 0x1B, 390 | } 391 | } 392 | } 393 | 394 | impl Into for TableKind { 395 | fn into(self) -> u32 { 396 | self.into_num() as u32 397 | } 398 | } 399 | 400 | impl Into for TableKind { 401 | fn into(self) -> usize { 402 | self.into_num() 403 | } 404 | } 405 | 406 | #[test] 407 | fn test_table_kind() { 408 | TableKind::into_table_kind(0x20).unwrap(); 409 | TableKind::into_table_kind(0x22).unwrap(); 410 | TableKind::into_table_kind(0x21).unwrap(); 411 | TableKind::into_table_kind(0x23).unwrap(); 412 | TableKind::into_table_kind(0x25).unwrap(); 413 | TableKind::into_table_kind(0x24).unwrap(); 414 | TableKind::into_table_kind(0x0F).unwrap(); 415 | TableKind::into_table_kind(0x0B).unwrap(); 416 | TableKind::into_table_kind(0x0C).unwrap(); 417 | TableKind::into_table_kind(0x0E).unwrap(); 418 | TableKind::into_table_kind(0x12).unwrap(); 419 | TableKind::into_table_kind(0x14).unwrap(); 420 | TableKind::into_table_kind(0x27).unwrap(); 421 | TableKind::into_table_kind(0x04).unwrap(); 422 | TableKind::into_table_kind(0x10).unwrap(); 423 | TableKind::into_table_kind(0x0D).unwrap(); 424 | TableKind::into_table_kind(0x1D).unwrap(); 425 | TableKind::into_table_kind(0x26).unwrap(); 426 | TableKind::into_table_kind(0x2A).unwrap(); 427 | TableKind::into_table_kind(0x2C).unwrap(); 428 | TableKind::into_table_kind(0x1C).unwrap(); 429 | TableKind::into_table_kind(0x09).unwrap(); 430 | TableKind::into_table_kind(0x28).unwrap(); 431 | TableKind::into_table_kind(0x0A).unwrap(); 432 | TableKind::into_table_kind(0x06).unwrap(); 433 | TableKind::into_table_kind(0x19).unwrap(); 434 | TableKind::into_table_kind(0x18).unwrap(); 435 | TableKind::into_table_kind(0x2B).unwrap(); 436 | TableKind::into_table_kind(0x00).unwrap(); 437 | TableKind::into_table_kind(0x1A).unwrap(); 438 | TableKind::into_table_kind(0x29).unwrap(); 439 | TableKind::into_table_kind(0x08).unwrap(); 440 | TableKind::into_table_kind(0x17).unwrap(); 441 | TableKind::into_table_kind(0x15).unwrap(); 442 | TableKind::into_table_kind(0x11).unwrap(); 443 | TableKind::into_table_kind(0x02).unwrap(); 444 | TableKind::into_table_kind(0x01).unwrap(); 445 | TableKind::into_table_kind(0x1B).unwrap(); 446 | assert_eq!(TableKind::into_table_kind(0xFF), None); 447 | 448 | assert_eq!(TableKind::Assembly.into_num(), 0x20); 449 | assert_eq!(TableKind::AssemblyOS.into_num(), 0x22); 450 | assert_eq!(TableKind::AssemblyProcessor.into_num(), 0x21); 451 | assert_eq!(TableKind::AssemblyRef.into_num(), 0x23); 452 | assert_eq!(TableKind::AssemblyRefOS.into_num(), 0x25); 453 | assert_eq!(TableKind::AssemblyRefProcessor.into_num(), 0x24); 454 | assert_eq!(TableKind::ClassLayout.into_num(), 0x0F); 455 | assert_eq!(TableKind::Constant.into_num(), 0x0B); 456 | assert_eq!(TableKind::CustomAttribute.into_num(), 0x0C); 457 | assert_eq!(TableKind::DeclSecurity.into_num(), 0x0E); 458 | assert_eq!(TableKind::EventMap.into_num(), 0x12); 459 | assert_eq!(TableKind::Event.into_num(), 0x14); 460 | assert_eq!(TableKind::ExportedType.into_num(), 0x27); 461 | assert_eq!(TableKind::Field.into_num(), 0x04); 462 | assert_eq!(TableKind::FieldLayout.into_num(), 0x10); 463 | assert_eq!(TableKind::FieldMarshal.into_num(), 0x0D); 464 | assert_eq!(TableKind::FieldRVA.into_num(), 0x1D); 465 | assert_eq!(TableKind::File.into_num(), 0x26); 466 | assert_eq!(TableKind::GenericParam.into_num(), 0x2A); 467 | assert_eq!(TableKind::GenericParamConstraint.into_num(), 0x2C); 468 | assert_eq!(TableKind::ImplMap.into_num(), 0x1C); 469 | assert_eq!(TableKind::InterfaceImpl.into_num(), 0x09); 470 | assert_eq!(TableKind::ManifestResource.into_num(), 0x28); 471 | assert_eq!(TableKind::MemberRef.into_num(), 0x0A); 472 | assert_eq!(TableKind::MethodDef.into_num(), 0x06); 473 | assert_eq!(TableKind::MethodImpl.into_num(), 0x19); 474 | assert_eq!(TableKind::MethodSemantics.into_num(), 0x18); 475 | assert_eq!(TableKind::MethodSpec.into_num(), 0x2B); 476 | assert_eq!(TableKind::Module.into_num(), 0x00); 477 | assert_eq!(TableKind::ModuleRef.into_num(), 0x1A); 478 | assert_eq!(TableKind::NestedClass.into_num(), 0x29); 479 | assert_eq!(TableKind::Param.into_num(), 0x08); 480 | assert_eq!(TableKind::Property.into_num(), 0x17); 481 | assert_eq!(TableKind::PropertyMap.into_num(), 0x15); 482 | assert_eq!(TableKind::StandAloneSig.into_num(), 0x11); 483 | assert_eq!(TableKind::TypeDef.into_num(), 0x02); 484 | assert_eq!(TableKind::TypeRef.into_num(), 0x01); 485 | assert_eq!(TableKind::TypeSpec.into_num(), 0x1B); 486 | } 487 | -------------------------------------------------------------------------------- /src/metadata/method.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | exec::instruction::Instruction, 3 | metadata::{class::*, signature::Type}, 4 | }; 5 | use std::{cell::RefCell, rc::Rc}; 6 | 7 | pub const TINY_FORMAT: u8 = 0x2; 8 | pub const FAT_FORMAT: u8 = 0x3; 9 | 10 | #[derive(Debug, Clone, PartialEq, Copy)] 11 | pub enum MethodHeaderType { 12 | TinyFormat { 13 | bytes: usize, 14 | }, 15 | FatFormat { 16 | flags: u16, 17 | size: u8, 18 | max_stack: u16, 19 | code_size: u32, 20 | local_var_sig_tok: u32, 21 | }, 22 | } 23 | 24 | pub type MethodInfoRef = Rc>; 25 | 26 | #[derive(Debug, Clone, PartialEq)] 27 | pub enum MethodInfo { 28 | MDef(MethodDefInfo), 29 | MRef(MemberRefInfo), 30 | } 31 | 32 | #[derive(Debug, Clone, PartialEq)] 33 | pub struct MethodDefInfo { 34 | pub rva: u32, 35 | pub impl_flags: u16, 36 | pub flags: u16, 37 | pub name: String, 38 | pub header_ty: MethodHeaderType, 39 | pub ty: Type, 40 | pub locals_ty: Vec, 41 | pub body: Vec, 42 | pub class: ClassInfoRef, 43 | } 44 | 45 | #[derive(Debug, Clone, PartialEq)] 46 | pub struct MemberRefInfo { 47 | pub name: String, 48 | pub ty: Type, 49 | pub class: ClassInfoRef, 50 | } 51 | 52 | impl MethodInfo { 53 | pub fn into_mdef(self) -> MethodDefInfo { 54 | match self { 55 | MethodInfo::MDef(m) => m, 56 | MethodInfo::MRef(_) => panic!(), 57 | } 58 | } 59 | 60 | pub fn into_mref(self) -> MemberRefInfo { 61 | match self { 62 | MethodInfo::MRef(m) => m, 63 | MethodInfo::MDef(_) => panic!(), 64 | } 65 | } 66 | 67 | pub fn as_mdef(&self) -> &MethodDefInfo { 68 | match self { 69 | MethodInfo::MDef(ref m) => m, 70 | MethodInfo::MRef(_) => panic!(), 71 | } 72 | } 73 | 74 | pub fn as_mref(&self) -> &MemberRefInfo { 75 | match self { 76 | MethodInfo::MRef(ref m) => m, 77 | MethodInfo::MDef(_) => panic!(), 78 | } 79 | } 80 | 81 | pub fn get_name(&self) -> &str { 82 | match self { 83 | MethodInfo::MDef(ref m) => m.name.as_str(), 84 | MethodInfo::MRef(ref m) => m.name.as_str(), 85 | } 86 | } 87 | 88 | pub fn get_class(&self) -> &ClassInfoRef { 89 | match self { 90 | MethodInfo::MDef(ref m) => &m.class, 91 | MethodInfo::MRef(ref m) => &m.class, 92 | } 93 | } 94 | 95 | pub fn is_static(&self) -> bool { 96 | match self { 97 | MethodInfo::MDef(ref m) => m.is_static(), 98 | // MemberRef has no info to determine whether it is static or not 99 | MethodInfo::MRef(_) => false, 100 | } 101 | } 102 | } 103 | 104 | impl MethodDefInfo { 105 | pub fn is_virtual(&self) -> bool { 106 | self.flags & method_attributes_flags::VIRTUAL > 0 107 | } 108 | 109 | pub fn is_static(&self) -> bool { 110 | self.flags & method_attributes_flags::STATIC > 0 111 | } 112 | 113 | pub fn is_new_slot(&self) -> bool { 114 | self.flags & method_attributes_flags::NEW_SLOT > 0 115 | } 116 | 117 | pub fn is_reuse_slot(&self) -> bool { 118 | self.flags & method_attributes_flags::NEW_SLOT == 0 119 | } 120 | } 121 | 122 | #[rustfmt::skip] 123 | pub mod method_attributes_flags { 124 | // TODO: Implement all the flags 125 | pub const STATIC : u16 = 0x0010; 126 | pub const VIRTUAL : u16 = 0x0040; 127 | pub const NEW_SLOT: u16 = 0x0100; 128 | } 129 | 130 | // #[derive(Debug, Clone)] 131 | // pub struct MemberRef { 132 | // name: String, 133 | // } 134 | -------------------------------------------------------------------------------- /src/metadata/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod assembly; 2 | pub mod class; 3 | pub mod header; 4 | pub mod image; 5 | pub mod metadata; 6 | pub mod method; 7 | pub mod pe_parser; 8 | pub mod signature; 9 | pub mod token; 10 | -------------------------------------------------------------------------------- /src/metadata/pe_parser.rs: -------------------------------------------------------------------------------- 1 | // TODO: What a dirty code 2 | 3 | use crate::exec::decode::BytesToInstructions; 4 | use crate::metadata::{ 5 | assembly::*, class::*, header::*, image::*, metadata::*, method::*, signature::*, 6 | }; 7 | use rustc_hash::FxHashMap; 8 | use std::{cell::RefCell, rc::Rc}; 9 | use std::{ 10 | fs::File, 11 | io::{BufReader, Read, Seek, SeekFrom}, 12 | iter, 13 | path::PathBuf, 14 | str::from_utf8, 15 | }; 16 | 17 | #[derive(Debug)] 18 | pub struct PEParser { 19 | reader: BufReader, 20 | filename: PathBuf, 21 | } 22 | 23 | macro_rules! try_eq { 24 | ($expr:expr) => {{ 25 | if !$expr { 26 | return None; 27 | } 28 | }}; 29 | } 30 | 31 | impl PEParser { 32 | pub fn new(filename: PathBuf) -> Option { 33 | Some(Self { 34 | reader: BufReader::new(match File::open(&filename) { 35 | Ok(file) => file, 36 | Err(_) => return None, 37 | }), 38 | filename: filename.to_path_buf(), 39 | }) 40 | } 41 | 42 | pub fn create_assembly(&mut self) -> Option { 43 | self.read_msdos_header()?; 44 | 45 | let pe_file_header = self.read_pe_file_header()?; 46 | 47 | let mut pe_optional_headers = vec![]; 48 | let pe_optional_header_size = 224; 49 | for _ in 0..pe_file_header.optional_header_size / pe_optional_header_size { 50 | let pe_optional_header = self.read_pe_optional_header()?; 51 | pe_optional_headers.push(pe_optional_header); 52 | } 53 | 54 | let mut sections = vec![]; 55 | for _ in 0..pe_file_header.number_of_sections { 56 | let section = self.read_section_header()?; 57 | sections.push(section); 58 | } 59 | 60 | let text_section = || -> Option<&SectionHeader> { 61 | for section in §ions { 62 | if section.name != ".text" { 63 | continue; 64 | } 65 | return Some(section); 66 | } 67 | None 68 | }()?; 69 | 70 | let cli_header_offset = text_section.pointer_to_raw_data as u64 + 8; /* CLI loader stub */ 71 | self.reader.seek(SeekFrom::Start(cli_header_offset)).ok()?; 72 | 73 | let cli_header = self.read_cli_header()?; 74 | 75 | let metadata_offset = (cli_header.metadata_rva - text_section.virtual_address 76 | + text_section.pointer_to_raw_data) as u64; 77 | self.reader.seek(SeekFrom::Start(metadata_offset)).ok()?; 78 | 79 | let metadata_header = self.read_metadata_header()?; 80 | dprintln!("MetaData header: {:?}", metadata_header); 81 | 82 | let mut stream_headers = vec![]; 83 | for _ in 0..metadata_header.streams { 84 | let stream = self.read_stream_header()?; 85 | stream_headers.push(stream); 86 | } 87 | 88 | dprintln!("Stream headers: {:?}", stream_headers); 89 | 90 | let metadata_streams = 91 | self.read_metadata_streams(metadata_offset, &metadata_header, &stream_headers)?; 92 | 93 | let image = Image::new( 94 | CLIInfo { 95 | cli_header, 96 | sections, 97 | }, 98 | metadata_streams, 99 | self.filename.clone(), 100 | None, 101 | ); 102 | 103 | let asm = retrieve!( 104 | image.metadata.metadata_stream.tables[TableKind::Assembly.into_num()] 105 | .get(0) 106 | .unwrap(), 107 | Table::Assembly 108 | ); 109 | let name = image.get_string(asm.name).to_string(); 110 | 111 | Some(Assembly { name, image }) 112 | } 113 | 114 | pub fn read_method( 115 | &mut self, 116 | image: &Image, 117 | class: &ClassInfoRef, 118 | rva: u32, 119 | ) -> Option { 120 | let text_section = image 121 | .cli_info 122 | .sections 123 | .iter() 124 | .find(|section| section.name == ".text") 125 | .unwrap(); 126 | let start = (rva - text_section.virtual_address + text_section.pointer_to_raw_data) as u64; 127 | self.read_method_body(image, class, rva, start) 128 | } 129 | 130 | fn read_method_body( 131 | &mut self, 132 | image: &Image, 133 | class: &ClassInfoRef, 134 | rva: u32, 135 | start: u64, 136 | ) -> Option { 137 | self.reader.seek(SeekFrom::Start(start)).ok()?; 138 | 139 | let (impl_flags, flags, name, ty) = unsafe { 140 | let MethodDefTable { 141 | name, 142 | signature, 143 | impl_flags, 144 | flags, 145 | .. 146 | } = image.get_method_def_table_by_rva(rva).unwrap(); 147 | let sig = image.metadata.blob.get(&(*signature as u32)).unwrap(); 148 | ( 149 | *impl_flags, 150 | *flags, 151 | image.get_string(*name).to_string(), 152 | SignatureParser::new(sig) 153 | .parse_method_def_sig(image) 154 | .unwrap(), 155 | ) 156 | }; 157 | 158 | let header_ty = self.read_method_header_type()?; 159 | 160 | match header_ty { 161 | MethodHeaderType::TinyFormat { bytes } => { 162 | let mut raw_body = vec![0u8; bytes]; 163 | self.read_bytes(raw_body.as_mut_slice())?; 164 | let body = BytesToInstructions::new(&raw_body).convert()?; 165 | Some(Rc::new(RefCell::new(MethodInfo::MDef(MethodDefInfo { 166 | rva, 167 | impl_flags, 168 | flags, 169 | name, 170 | header_ty, 171 | body, 172 | locals_ty: vec![], 173 | ty, 174 | class: class.clone(), 175 | })))) 176 | } 177 | MethodHeaderType::FatFormat { 178 | code_size, 179 | local_var_sig_tok, 180 | .. 181 | } => { 182 | let locals_ty = match image.metadata.get_table_entry(local_var_sig_tok) { 183 | Some(sig) => match sig { 184 | Table::StandAloneSig(sast) => { 185 | let mut blob = image 186 | .metadata 187 | .blob 188 | .get(&(sast.signature as u32)) 189 | .unwrap() 190 | .iter(); 191 | assert_eq!(blob.next()?, &0x07); 192 | let len = *blob.next()? as usize; 193 | use std::iter::repeat_with; 194 | repeat_with(|| Type::into_type(image, &mut blob).unwrap()) 195 | .take(len) 196 | .collect() 197 | } 198 | _ => unimplemented!(), 199 | }, 200 | None => vec![], 201 | }; 202 | 203 | let mut raw_body = vec![0u8; code_size as usize]; 204 | self.read_bytes(raw_body.as_mut_slice())?; 205 | let body = BytesToInstructions::new(&raw_body).convert()?; 206 | 207 | Some(Rc::new(RefCell::new(MethodInfo::MDef(MethodDefInfo { 208 | rva, 209 | impl_flags, 210 | flags, 211 | name, 212 | header_ty, 213 | body, 214 | locals_ty, 215 | ty, 216 | class: class.clone(), 217 | })))) 218 | } 219 | } 220 | } 221 | 222 | fn read_method_header_type(&mut self) -> Option { 223 | let first = self.read_u8()?; 224 | match first & 0b11 { 225 | TINY_FORMAT => Some(MethodHeaderType::TinyFormat { 226 | bytes: first as usize >> 2, 227 | }), 228 | FAT_FORMAT => { 229 | // TODO: More flags 230 | let flags_size = self.read_u8()?; 231 | let size = flags_size & 0b1111; 232 | let max_stack = self.read_u16()?; 233 | let code_size = self.read_u32()?; 234 | let local_var_sig_tok = self.read_u32()?; 235 | Some(MethodHeaderType::FatFormat { 236 | flags: ((first as u16) << 8) + ((flags_size as u16) >> 4), 237 | size, 238 | max_stack, 239 | code_size, 240 | local_var_sig_tok, 241 | }) 242 | } 243 | _ => None, 244 | } 245 | } 246 | 247 | fn read_msdos_header(&mut self) -> Option<()> { 248 | let mut first = [0u8; 60]; 249 | self.read_bytes(&mut first)?; 250 | 251 | try_eq!( 252 | &first[..] 253 | == &[ 254 | 0x4d, 0x5a, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 255 | 0xFF, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 256 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 257 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 258 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 259 | ][..] 260 | ); 261 | 262 | let _lfanew = self.read_u32()?; 263 | 264 | let mut latter = [0u8; 64]; 265 | self.read_bytes(&mut latter)?; 266 | 267 | try_eq!( 268 | &latter[..] 269 | == &[ 270 | 0x0e, 0x1f, 0xba, 0x0e, 0x00, 0xb4, 0x09, 0xcd, 0x21, 0xb8, 0x01, 0x4c, 0xcd, 271 | 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x61, 0x6d, 272 | 0x20, 0x63, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 273 | 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x44, 0x4f, 0x53, 0x20, 0x6d, 0x6f, 0x64, 0x65, 274 | 0x2e, 0x0d, 0x0d, 0x0a, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 275 | ][..] 276 | ); 277 | 278 | Some(()) 279 | } 280 | 281 | fn read_pe_file_header(&mut self) -> Option { 282 | let mut pe_signature = [0u8; 4]; 283 | self.read_bytes(&mut pe_signature)?; 284 | try_eq!(&pe_signature[..] == &['P' as u8, 'E' as u8, 0, 0][..]); 285 | 286 | let pe_file_header = self.read_struct::()?; 287 | try_eq!(pe_file_header.machine == 0x14c); 288 | try_eq!(pe_file_header.pointer_to_symbol_table == 0); 289 | try_eq!(pe_file_header.number_of_symbols == 0); 290 | 291 | Some(pe_file_header) 292 | } 293 | 294 | fn read_pe_optional_header(&mut self) -> Option { 295 | let magic = self.read_u16()?; 296 | try_eq!(magic == 0x10b); 297 | 298 | let _lmajor = self.read_u8()?; 299 | // println!("{}", lmajor); 300 | // try_eq!(lmajor == 6); 301 | 302 | let lminor = self.read_u8()?; 303 | // println!("{}", lminor); 304 | try_eq!(lminor == 0); 305 | 306 | let code_size = self.read_u32()?; 307 | 308 | let initialized_data_size = self.read_u32()?; 309 | 310 | let uninitialized_data_size = self.read_u32()?; 311 | 312 | let entry_point_rva = self.read_u32()?; 313 | 314 | let base_of_code = self.read_u32()?; 315 | 316 | let base_of_data = self.read_u32()?; 317 | 318 | let image_base = self.read_u32()?; 319 | 320 | let section_alignment = self.read_u32()?; 321 | 322 | let file_alignment = self.read_u32()?; 323 | try_eq!(file_alignment == 0x200); 324 | 325 | let os_major = self.read_u16()?; 326 | try_eq!(os_major == 5 || os_major == 4); 327 | 328 | let os_minor = self.read_u16()?; 329 | try_eq!(os_minor == 0); 330 | 331 | let user_major = self.read_u16()?; 332 | try_eq!(user_major == 0); 333 | 334 | let user_minor = self.read_u16()?; 335 | try_eq!(user_minor == 0); 336 | 337 | let subsys_major = self.read_u16()?; 338 | try_eq!(subsys_major == 5 || subsys_major == 4); 339 | 340 | let subsys_minor = self.read_u16()?; 341 | try_eq!(subsys_minor == 0); 342 | 343 | let reserved = self.read_u32()?; 344 | try_eq!(reserved == 0); 345 | 346 | let image_size = self.read_u32()?; 347 | 348 | let header_size = self.read_u32()?; 349 | 350 | let file_checksum = self.read_u32()?; 351 | try_eq!(file_checksum == 0); 352 | 353 | let sub_system = self.read_u16()?; 354 | 355 | let dll_flags = self.read_u16()?; 356 | 357 | let stack_reserve_size = self.read_u32()?; 358 | 359 | let stack_commit_size = self.read_u32()?; 360 | 361 | let heap_reserve_size = self.read_u32()?; 362 | 363 | let heap_commit_size = self.read_u32()?; 364 | 365 | let loader_flags = self.read_u32()?; 366 | 367 | let number_of_data_directories = self.read_u32()?; 368 | 369 | let export_table = self.read_u64()?; 370 | try_eq!(export_table == 0); 371 | 372 | let import_table_rva = self.read_u32()?; 373 | 374 | let import_table_size = self.read_u32()?; 375 | 376 | let _resource_table = self.read_u64()?; 377 | // try_eq!(resource_table == 0); 378 | 379 | let exception_table = self.read_u64()?; 380 | try_eq!(exception_table == 0); 381 | 382 | let certification_table = self.read_u64()?; 383 | try_eq!(certification_table == 0); 384 | 385 | let base_relocation_table_rva = self.read_u32()?; 386 | 387 | let base_relocation_table_size = self.read_u32()?; 388 | 389 | let debug = self.read_u64()?; 390 | try_eq!(debug == 0); 391 | 392 | let copyright = self.read_u64()?; 393 | try_eq!(copyright == 0); 394 | 395 | let global_ptr = self.read_u64()?; 396 | try_eq!(global_ptr == 0); 397 | 398 | let tls_table = self.read_u64()?; 399 | try_eq!(tls_table == 0); 400 | 401 | let load_config_table = self.read_u64()?; 402 | try_eq!(load_config_table == 0); 403 | 404 | let bound_import = self.read_u64()?; 405 | try_eq!(bound_import == 0); 406 | 407 | let iat_rva = self.read_u32()?; 408 | 409 | let iat_size = self.read_u32()?; 410 | 411 | let delay_import_descriptor = self.read_u64()?; 412 | try_eq!(delay_import_descriptor == 0); 413 | 414 | let cli_header_rva = self.read_u32()?; 415 | 416 | let cli_header_size = self.read_u32()?; 417 | 418 | let reserved = self.read_u64()?; 419 | try_eq!(reserved == 0); 420 | 421 | Some(PEOptionalHeader { 422 | code_size, 423 | initialized_data_size, 424 | uninitialized_data_size, 425 | entry_point_rva, 426 | base_of_code, 427 | base_of_data, 428 | image_base, 429 | section_alignment, 430 | image_size, 431 | header_size, 432 | sub_system, 433 | dll_flags, 434 | stack_reserve_size, 435 | stack_commit_size, 436 | heap_reserve_size, 437 | heap_commit_size, 438 | loader_flags, 439 | number_of_data_directories, 440 | import_table_rva, 441 | import_table_size, 442 | base_relocation_table_rva, 443 | base_relocation_table_size, 444 | iat_rva, 445 | iat_size, 446 | cli_header_rva, 447 | cli_header_size, 448 | }) 449 | } 450 | 451 | fn read_section_header(&mut self) -> Option { 452 | let mut name_bytes = [0u8; 8]; 453 | self.read_bytes(&mut name_bytes)?; 454 | let name = name_bytes 455 | .iter() 456 | .take_while(|b| **b != 0) 457 | .map(|&s| s as char) 458 | .collect::(); 459 | 460 | let virtual_size = self.read_u32()?; 461 | 462 | let virtual_address = self.read_u32()?; 463 | 464 | let size_of_raw_data = self.read_u32()?; 465 | 466 | let pointer_to_raw_data = self.read_u32()?; 467 | 468 | let pointer_to_relocations = self.read_u32()?; 469 | 470 | let pointer_to_linenumbers = self.read_u32()?; 471 | 472 | let number_of_relocations = self.read_u16()?; 473 | 474 | let number_of_linenumbers = self.read_u16()?; 475 | 476 | let characteristics = self.read_u32()?; 477 | 478 | Some(SectionHeader { 479 | name, 480 | virtual_size, 481 | virtual_address, 482 | size_of_raw_data, 483 | pointer_to_raw_data, 484 | pointer_to_relocations, 485 | pointer_to_linenumbers, 486 | number_of_relocations, 487 | number_of_linenumbers, 488 | characteristics, 489 | }) 490 | } 491 | 492 | fn read_cli_header(&mut self) -> Option { 493 | let cb = self.read_u32()?; 494 | 495 | let major_runtime_version = self.read_u16()?; 496 | 497 | let minor_runtime_version = self.read_u16()?; 498 | 499 | let metadata_rva = self.read_u32()?; 500 | 501 | let metadata_size = self.read_u32()?; 502 | 503 | let flags = self.read_u32()?; 504 | 505 | let entry_point_token = self.read_u32()?; 506 | 507 | let resources_rva = self.read_u32()?; 508 | 509 | let resources_size = self.read_u32()?; 510 | 511 | let strong_name_signature_rva = self.read_u32()?; 512 | 513 | let strong_name_signature_version = self.read_u32()?; 514 | 515 | let code_manager_table = self.read_u64()?; 516 | try_eq!(code_manager_table == 0); 517 | 518 | let vtable_fixups_virtual_address = self.read_u32()?; 519 | 520 | let vtable_fixups_size = self.read_u16()?; 521 | 522 | let vtable_fixups_type = self.read_u16()?; 523 | 524 | Some(CLIHeader { 525 | cb, 526 | major_runtime_version, 527 | minor_runtime_version, 528 | metadata_rva, 529 | metadata_size, 530 | flags, 531 | entry_point_token, 532 | resources_rva, 533 | resources_size, 534 | strong_name_signature_rva, 535 | strong_name_signature_version, 536 | vtable_fixups_virtual_address, 537 | vtable_fixups_size, 538 | vtable_fixups_type, 539 | }) 540 | } 541 | 542 | fn read_metadata_header(&mut self) -> Option { 543 | let signature = self.read_u32()?; 544 | try_eq!(signature == 0x424A_5342); 545 | 546 | let _major_version = self.read_u16()?; 547 | let _minor_version = self.read_u16()?; 548 | 549 | let reserved = self.read_u32()?; 550 | try_eq!(reserved == 0); 551 | 552 | let length = self.read_u32()?; 553 | 554 | let mut version_raw = vec![0u8; length as usize]; 555 | self.read_bytes(version_raw.as_mut_slice())?; 556 | let version = version_raw 557 | .iter() 558 | .take_while(|b| **b != 0) 559 | .map(|&c| c as char) 560 | .collect::(); 561 | 562 | let flags = self.read_u16()?; 563 | try_eq!(flags == 0); 564 | 565 | let streams = self.read_u16()?; 566 | 567 | Some(MetaDataHeader { version, streams }) 568 | } 569 | 570 | fn read_stream_header(&mut self) -> Option { 571 | let offset = self.read_u32()?; 572 | 573 | let size = self.read_u32()?; 574 | 575 | let mut name = "".to_string(); 576 | let mut count = 1; 577 | loop { 578 | let c = self.read_u8()?; 579 | if c == 0 { 580 | while count % 4 != 0 { 581 | self.read_u8()?; 582 | count += 1; 583 | } 584 | break; 585 | } 586 | name.push(c as char); 587 | count += 1; 588 | } 589 | 590 | Some(StreamHeader { offset, size, name }) 591 | } 592 | 593 | fn read_metadata_stream(&mut self) -> Option { 594 | let reserved = self.read_u32()?; 595 | try_eq!(reserved == 0); 596 | 597 | let major_version = self.read_u8()?; 598 | 599 | let minor_version = self.read_u8()?; 600 | 601 | let heap_sizes = self.read_u8()?; 602 | 603 | let _reserved = self.read_u8()?; 604 | // try_eq!(reserved == 1); 605 | 606 | let valid = self.read_u64()?; 607 | 608 | let sorted = self.read_u64()?; 609 | 610 | let n = valid.count_ones(); 611 | 612 | let mut rows = vec![]; 613 | for _ in 0..n { 614 | let row = self.read_u32()?; 615 | rows.push(row); 616 | } 617 | 618 | let table_kinds = TableKind::table_kinds(valid); 619 | let tables = self.read_metadata_tables(&table_kinds, &rows)?; 620 | 621 | Some(MetaDataStream { 622 | major_version, 623 | minor_version, 624 | heap_sizes, 625 | valid, 626 | sorted, 627 | rows, 628 | tables, 629 | }) 630 | } 631 | 632 | fn read_metadata_tables( 633 | &mut self, 634 | table_kinds: &[TableKind], 635 | rows: &[u32], 636 | ) -> Option>> { 637 | let mut tables: Vec> = iter::repeat_with(|| vec![]).take(NUM_TABLES).collect(); 638 | 639 | for (i, kind) in table_kinds.iter().enumerate() { 640 | let num = rows[i]; 641 | for _ in 0..num { 642 | tables[kind.into_num()].push(match kind { 643 | TableKind::Module => Table::Module(self.read_struct::()?), 644 | TableKind::TypeRef => Table::TypeRef(self.read_struct::()?), 645 | TableKind::TypeDef => Table::TypeDef(self.read_struct::()?), 646 | TableKind::MethodDef => Table::MethodDef(self.read_struct::()?), 647 | TableKind::MemberRef => Table::MemberRef(self.read_struct::()?), 648 | TableKind::CustomAttribute => { 649 | Table::CustomAttribute(self.read_struct::()?) 650 | } 651 | TableKind::Assembly => Table::Assembly(self.read_struct::()?), 652 | TableKind::AssemblyRef => { 653 | Table::AssemblyRef(self.read_struct::()?) 654 | } 655 | TableKind::Constant => Table::Constant(self.read_struct::()?), 656 | TableKind::Param => Table::Param(self.read_struct::()?), 657 | TableKind::StandAloneSig => { 658 | Table::StandAloneSig(self.read_struct::()?) 659 | } 660 | TableKind::Field => Table::Field(self.read_struct::()?), 661 | e => unimplemented!("{:?}", e), 662 | }) 663 | } 664 | } 665 | 666 | Some(tables) 667 | } 668 | 669 | // Return (length, read bytes) 670 | fn read_blob_length(&mut self) -> Option<(u32, u32)> { 671 | let first = self.read_u8()? as u32; 672 | if first & 0b1000_0000 == 0 { 673 | Some((first & 0b0111_1111, 1)) 674 | } else if first & 0b1000_0000 > 0 { 675 | Some((((first & 0b0111_1111) << 8) + self.read_u8()? as u32, 2)) 676 | } else if first & 0b1100_0000 > 0 { 677 | let x = self.read_u8()? as u32; 678 | let y = self.read_u8()? as u32; 679 | let z = self.read_u8()? as u32; 680 | Some((((first & 0b0011_1111) << 24) + (x << 16) + (y << 8) + z, 4)) 681 | } else { 682 | None 683 | } 684 | } 685 | 686 | fn read_metadata_streams( 687 | &mut self, 688 | metadata_offset: u64, 689 | metadata_header: &MetaDataHeader, 690 | stream_headers: &[StreamHeader], 691 | ) -> Option { 692 | // Some of followings are not streams but heaps. 693 | let mut metadata_stream = None; 694 | let mut strings = None; 695 | let mut user_strings = None; 696 | let mut blob = None; 697 | let mut guid = None; 698 | 699 | for i in 0..metadata_header.streams as usize { 700 | self.reader 701 | .seek(SeekFrom::Start( 702 | metadata_offset + stream_headers[i].offset as u64, 703 | )) 704 | .ok()?; 705 | match stream_headers[i].name.as_str() { 706 | "#~" => metadata_stream = self.read_metadata_streams_metadata_stream(), 707 | "#Strings" => strings = self.read_metadata_streams_strings(&stream_headers[i]), 708 | "#US" => user_strings = self.read_metadata_streams_user_strings(&stream_headers[i]), 709 | "#Blob" => blob = self.read_metadata_streams_blob(&stream_headers[i]), 710 | "#GUID" => guid = self.read_metadata_streams_guid(), 711 | _ => unreachable!(), 712 | } 713 | } 714 | 715 | Some(MetaDataStreams { 716 | metadata_stream: metadata_stream?, 717 | strings: strings?, 718 | user_strings: user_strings?, 719 | blob: blob?, 720 | guid: guid?, 721 | }) 722 | } 723 | 724 | fn read_metadata_streams_metadata_stream(&mut self) -> Option { 725 | let stream = self.read_metadata_stream()?; 726 | dprintln!("#~ stream: {:?}", stream); 727 | Some(stream) 728 | } 729 | 730 | fn read_metadata_streams_strings( 731 | &mut self, 732 | sh: &StreamHeader, 733 | ) -> Option> { 734 | let mut strings = FxHashMap::default(); 735 | let mut bytes = vec![]; 736 | let mut bgn = 0; 737 | 738 | for i in 0..sh.size { 739 | let c = self.read_u8()?; 740 | if c == 0 { 741 | strings.insert(bgn, from_utf8(&bytes).unwrap().to_string()); 742 | bytes.clear(); 743 | bgn = i + 1; 744 | } else { 745 | bytes.push(c); 746 | } 747 | } 748 | 749 | when_debug!(for (i, s) in &strings { 750 | dprintln!("#Strings({:02X}): '{}'", i, s) 751 | }); 752 | 753 | Some(strings) 754 | } 755 | 756 | fn read_metadata_streams_user_strings( 757 | &mut self, 758 | sh: &StreamHeader, 759 | ) -> Option>> { 760 | let mut user_strings = FxHashMap::default(); 761 | let mut bytes = vec![]; 762 | let mut count = 0; 763 | 764 | while count < sh.size { 765 | let bgn = count; 766 | let (len, read_bytes) = self.read_blob_length()?; 767 | count += read_bytes; 768 | 769 | /* read 2 bytes each so divide len by 2 */ 770 | for _ in 0..len / 2 { 771 | bytes.push(self.read_u16()?); 772 | } 773 | 774 | if len % 2 == 1 { 775 | // Consume additional one byte 776 | self.read_u8()?; // TODO 777 | } 778 | 779 | user_strings.insert(bgn as u32, bytes.clone()); 780 | bytes.clear(); 781 | count += len as u32; 782 | } 783 | 784 | when_debug!(for (i, us) in &user_strings { 785 | dprintln!("#US({:02X}): '{}'", i, String::from_utf16(&us).ok()?) 786 | }); 787 | 788 | Some(user_strings) 789 | } 790 | 791 | fn read_metadata_streams_blob(&mut self, sh: &StreamHeader) -> Option>> { 792 | let mut blob = FxHashMap::default(); 793 | let mut bytes = vec![]; 794 | let mut count = 0; 795 | 796 | while count < sh.size { 797 | let bgn = count; 798 | let (len, read_bytes) = self.read_blob_length()?; 799 | count += read_bytes; 800 | 801 | for _ in 0..len { 802 | bytes.push(self.read_u8()?); 803 | } 804 | 805 | blob.insert(bgn as u32, bytes.clone()); 806 | bytes.clear(); 807 | count += len as u32; 808 | } 809 | 810 | when_debug!(for (i, b) in &blob { 811 | dprintln!("#Blob({:02X}): {:?}", i, b) 812 | }); 813 | 814 | Some(blob) 815 | } 816 | 817 | fn read_metadata_streams_guid(&mut self) -> Option { 818 | let data1 = self.read_u32()?; 819 | let data2 = self.read_u16()?; 820 | let data3 = self.read_u16()?; 821 | let mut data4 = [0u8; 8]; 822 | self.read_bytes(&mut data4)?; 823 | 824 | let guid = format!( 825 | "{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}", 826 | data1, 827 | data2, 828 | data3, 829 | data4[0], 830 | data4[1], 831 | data4[2], 832 | data4[3], 833 | data4[4], 834 | data4[5], 835 | data4[6], 836 | data4[7], 837 | ); 838 | 839 | dprintln!("#GUID: {}", guid); 840 | 841 | Some(guid) 842 | } 843 | 844 | fn read_struct(&mut self) -> Option { 845 | unsafe { 846 | let size = ::std::mem::size_of::(); 847 | let mut strct: T = ::std::mem::zeroed(); 848 | let strct_slice = 849 | ::std::slice::from_raw_parts_mut(&mut strct as *mut _ as *mut u8, size); 850 | self.reader.read_exact(strct_slice).ok()?; 851 | Some(strct) 852 | } 853 | } 854 | } 855 | 856 | impl PEParser { 857 | fn read_bytes(&mut self, buf: &mut [u8]) -> Option<()> { 858 | match self.reader.read_exact(buf) { 859 | Ok(()) => Some(()), 860 | Err(_) => None, 861 | } 862 | } 863 | 864 | fn read_u64(&mut self) -> Option { 865 | let mut buf = [0u8; 8]; 866 | match self.reader.read_exact(&mut buf) { 867 | Ok(()) => Some( 868 | ((buf[7] as u64) << 56) 869 | + ((buf[6] as u64) << 48) 870 | + ((buf[5] as u64) << 40) 871 | + ((buf[4] as u64) << 32) 872 | + ((buf[3] as u64) << 24) 873 | + ((buf[2] as u64) << 16) 874 | + ((buf[1] as u64) << 8) 875 | + buf[0] as u64, 876 | ), 877 | Err(_) => None, 878 | } 879 | } 880 | 881 | fn read_u32(&mut self) -> Option { 882 | let mut buf = [0u8; 4]; 883 | match self.reader.read_exact(&mut buf) { 884 | Ok(()) => Some( 885 | ((buf[3] as u32) << 24) 886 | + ((buf[2] as u32) << 16) 887 | + ((buf[1] as u32) << 8) 888 | + buf[0] as u32, 889 | ), 890 | Err(_) => None, 891 | } 892 | } 893 | 894 | fn read_u16(&mut self) -> Option { 895 | let mut buf = [0u8; 2]; 896 | match self.reader.read_exact(&mut buf) { 897 | Ok(()) => Some(((buf[1] as u16) << 8) + buf[0] as u16), 898 | Err(_) => None, 899 | } 900 | } 901 | 902 | fn read_u8(&mut self) -> Option { 903 | let mut buf = [0u8; 1]; 904 | match self.reader.read_exact(&mut buf) { 905 | Ok(()) => Some(buf[0]), 906 | Err(_) => None, 907 | } 908 | } 909 | } 910 | -------------------------------------------------------------------------------- /src/metadata/signature.rs: -------------------------------------------------------------------------------- 1 | use crate::metadata::{class::*, image::*, token::*}; 2 | use std::{fmt, iter::repeat_with, slice::Iter}; 3 | 4 | #[derive(Debug, Clone, PartialEq)] 5 | pub struct Type { 6 | pub base: ElementType, 7 | } 8 | 9 | #[derive(Clone, PartialEq)] 10 | pub enum ElementType { 11 | Void, 12 | Boolean, 13 | Char, 14 | I4, 15 | U4, 16 | I8, 17 | R8, 18 | String, 19 | Class(ClassInfoRef), 20 | SzArray(Box), 21 | FnPtr(Box), 22 | Ptr(Box), 23 | Object, 24 | ValueType(ClassInfoRef), 25 | } 26 | 27 | #[derive(Debug, Clone, PartialEq)] 28 | pub struct MethodSignature { 29 | /// Represent ``HASTHIS``, ``EXPLICITTHIS``, ``DEFAULT``, ``GENERIC`` and ``VARARG`` 30 | pub info: u8, 31 | 32 | /// Return type 33 | pub ret: Type, 34 | 35 | /// Parameters' types 36 | pub params: Vec, 37 | } 38 | 39 | #[derive(Debug, Clone, PartialEq)] 40 | pub struct SzArrayInfo { 41 | /// Array's element type 42 | pub elem_ty: Type, 43 | } 44 | 45 | #[derive(Debug, Clone)] 46 | pub struct SignatureParser<'a> { 47 | sig: Iter<'a, u8>, 48 | } 49 | 50 | impl Type { 51 | pub fn new(base: ElementType) -> Self { 52 | Self { base } 53 | } 54 | 55 | pub fn void_ty() -> Self { 56 | Self::new(ElementType::Void) 57 | } 58 | 59 | pub fn string_ty() -> Self { 60 | Self::new(ElementType::String) 61 | } 62 | 63 | pub fn boolean_ty() -> Self { 64 | Self::new(ElementType::Boolean) 65 | } 66 | 67 | pub fn char_ty() -> Self { 68 | Self::new(ElementType::Char) 69 | } 70 | 71 | pub fn i4_ty() -> Self { 72 | Self::new(ElementType::I4) 73 | } 74 | 75 | pub fn u4_ty() -> Self { 76 | Self::new(ElementType::U4) 77 | } 78 | 79 | pub fn i8_ty() -> Self { 80 | Self::new(ElementType::I8) 81 | } 82 | 83 | pub fn r8_ty() -> Self { 84 | Self::new(ElementType::R8) 85 | } 86 | 87 | pub fn full_method_ty(flags: u8, ret: Type, params: &[Type]) -> Self { 88 | Self::new(ElementType::FnPtr(Box::new(MethodSignature { 89 | info: flags, 90 | ret, 91 | params: params.to_vec(), 92 | }))) 93 | } 94 | 95 | pub fn i4_szarr_ty() -> Self { 96 | Self::new(ElementType::SzArray(Box::new(SzArrayInfo { 97 | elem_ty: Type::i4_ty(), 98 | }))) 99 | } 100 | 101 | pub fn boolean_szarr_ty() -> Self { 102 | Self::new(ElementType::SzArray(Box::new(SzArrayInfo { 103 | elem_ty: Type::boolean_ty(), 104 | }))) 105 | } 106 | 107 | pub fn object_szarr_ty() -> Self { 108 | Self::new(ElementType::SzArray(Box::new(SzArrayInfo { 109 | elem_ty: Type::object_ty(), 110 | }))) 111 | } 112 | 113 | pub fn szarr_ty(elem_ty: Type) -> Self { 114 | Self::new(ElementType::SzArray(Box::new(SzArrayInfo { elem_ty }))) 115 | } 116 | 117 | pub fn class_ty(class: ClassInfoRef) -> Self { 118 | Self::new(ElementType::Class(class)) 119 | } 120 | 121 | pub fn object_ty() -> Self { 122 | Self::new(ElementType::Object) 123 | } 124 | 125 | pub fn ptr_ty(elem: Type) -> Self { 126 | Self::new(ElementType::Ptr(Box::new(elem))) 127 | } 128 | 129 | pub fn into_type<'a>(image: &Image, sig: &mut Iter<'a, u8>) -> Option { 130 | match sig.next()? { 131 | 0x01 => Some(Type::new(ElementType::Void)), 132 | 0x02 => Some(Type::new(ElementType::Boolean)), 133 | 0x03 => Some(Type::new(ElementType::Char)), 134 | 0x08 => Some(Type::new(ElementType::I4)), 135 | 0x09 => Some(Type::new(ElementType::U4)), 136 | 0x0a => Some(Type::new(ElementType::I8)), 137 | 0x0d => Some(Type::new(ElementType::R8)), 138 | 0x0e => Some(Type::new(ElementType::String)), 139 | 0x12 => Type::class_into_type(image, sig), 140 | 0x1c => Some(Type::new(ElementType::Object)), 141 | 0x1d => Some(Type::new(ElementType::SzArray(Box::new(SzArrayInfo { 142 | elem_ty: Type::into_type(image, sig)?, 143 | })))), 144 | 0x11 => Type::valuetype_into_type(image, sig), 145 | // TODO 146 | e => unimplemented!("{:?}", e), 147 | // _ => None, 148 | } 149 | } 150 | 151 | fn class_into_type<'a>(image: &Image, sig: &mut Iter<'a, u8>) -> Option { 152 | let token = decompress_uint(sig).unwrap(); 153 | let class_ref = image 154 | .class_cache 155 | .get(&decode_typedef_or_ref_token(token).into()) 156 | .unwrap(); 157 | Some(Type::new(ElementType::Class(class_ref.clone()))) 158 | } 159 | 160 | fn valuetype_into_type<'a>(image: &Image, sig: &mut Iter<'a, u8>) -> Option { 161 | let token = decompress_uint(sig).unwrap(); 162 | let class_ref = image 163 | .class_cache 164 | .get(&decode_typedef_or_ref_token(token).into()) 165 | .unwrap(); 166 | Some(Type::new(ElementType::ValueType(class_ref.clone()))) 167 | } 168 | 169 | pub fn as_fnptr(&self) -> Option<&MethodSignature> { 170 | match self.base { 171 | ElementType::FnPtr(ref fnptr) => Some(fnptr), 172 | _ => None, 173 | } 174 | } 175 | 176 | pub fn as_class(&self) -> Option<&ClassInfoRef> { 177 | match self.base { 178 | ElementType::Class(ref class) => Some(class), 179 | _ => None, 180 | } 181 | } 182 | 183 | pub fn as_szarray(&self) -> Option<&SzArrayInfo> { 184 | match self.base { 185 | ElementType::SzArray(ref szarr) => Some(szarr), 186 | _ => None, 187 | } 188 | } 189 | 190 | pub fn equal_method(&self, ret: ElementType, params: &[ElementType]) -> bool { 191 | match self.base { 192 | ElementType::FnPtr(ref ms) => { 193 | ms.ret.base == ret && ms.params.iter().zip(params).all(|(p, q)| &p.base == q) 194 | } 195 | _ => false, 196 | } 197 | } 198 | 199 | pub fn is_void(&self) -> bool { 200 | self.base == ElementType::Void 201 | } 202 | 203 | pub fn is_int(&self) -> bool { 204 | match self.base { 205 | // Is Bool int? 206 | ElementType::Char | ElementType::I4 | ElementType::I8 | ElementType::U4 => true, 207 | ElementType::ValueType(ref c) if c.borrow().is_enum() => true, 208 | _ => false, 209 | } 210 | } 211 | 212 | pub fn is_float(&self) -> bool { 213 | match self.base { 214 | ElementType::R8 => true, 215 | _ => false, 216 | } 217 | } 218 | } 219 | 220 | impl<'a> SignatureParser<'a> { 221 | pub fn new(sig: &'a [u8]) -> Self { 222 | Self { sig: sig.iter() } 223 | } 224 | 225 | pub fn parse_method_ref_sig(&mut self, image: &Image) -> Option { 226 | let first = *self.sig.next()?; 227 | let _has_this = first & 0x20; 228 | let _explicit_this = first & 0x40; 229 | let _var_arg = first & 0x5; 230 | 231 | let param_count = decompress_uint(&mut self.sig)?; 232 | let ret = Type::into_type(image, &mut self.sig)?; 233 | 234 | let params = repeat_with(|| Type::into_type(image, &mut self.sig).unwrap()) 235 | .take(param_count as usize) 236 | .collect(); 237 | 238 | let _var_arg_params = if let Some(0x41) = self.sig.next() { 239 | repeat_with(|| Type::into_type(image, &mut self.sig).unwrap()) 240 | .take(param_count as usize) 241 | .collect() 242 | } else { 243 | vec![] 244 | }; 245 | 246 | Some(Type::new(ElementType::FnPtr(Box::new(MethodSignature { 247 | info: first, 248 | ret, 249 | params, 250 | })))) 251 | } 252 | 253 | pub fn parse_method_def_sig(&mut self, image: &Image) -> Option { 254 | let first = *self.sig.next()?; 255 | let _default = first == 0; 256 | let _has_this = first & 0x20; 257 | let _explicit_this = first & 0x40; 258 | let _var_arg = first & 0x5; 259 | let _generic = first & 0x10; 260 | 261 | let param_count = decompress_uint(&mut self.sig)?; 262 | let ret = Type::into_type(image, &mut self.sig)?; 263 | 264 | let params = repeat_with(|| Type::into_type(image, &mut self.sig).unwrap()) 265 | .take(param_count as usize) 266 | .collect(); 267 | 268 | Some(Type::new(ElementType::FnPtr(Box::new(MethodSignature { 269 | info: first, 270 | ret, 271 | params, 272 | })))) 273 | } 274 | } 275 | 276 | impl MethodSignature { 277 | pub fn has_this(&self) -> bool { 278 | self.info & 0x20 > 0 279 | } 280 | } 281 | 282 | impl fmt::Debug for ElementType { 283 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 284 | write!( 285 | f, 286 | "ElementType::{}", 287 | match self { 288 | ElementType::Class(c) => format!("Class({})", c.borrow().name), 289 | ElementType::Void => "Void".to_string(), 290 | ElementType::Boolean => "Boolean".to_string(), 291 | ElementType::Char => "Char".to_string(), 292 | ElementType::I4 => "I4".to_string(), 293 | ElementType::U4 => "U4".to_string(), 294 | ElementType::I8 => "I8".to_string(), 295 | ElementType::R8 => "R8".to_string(), 296 | ElementType::String => "String".to_string(), 297 | ElementType::SzArray(s) => format!("SzArray({:?})", s), 298 | ElementType::FnPtr(f) => format!("FnPtr({:?})", f), 299 | ElementType::Ptr(e) => format!("Ptr({:?})", e), 300 | ElementType::Object => format!("Object"), 301 | ElementType::ValueType(c) => format!("ValueType({})", c.borrow().name), 302 | } 303 | ) 304 | } 305 | } 306 | 307 | pub fn decompress_uint<'a>(sig: &mut Iter<'a, u8>) -> Option { 308 | let x = *sig.next()? as u32; 309 | if x & 0b1000_0000 == 0 { 310 | // 1 byte 311 | Some(x) 312 | } else if x & 0b1000_0000 > 0 { 313 | // 2 bytes 314 | let y = *sig.next()? as u32; 315 | Some(((x & 0b0011_1111) << 8) + y) 316 | } else { 317 | // 4 bytes 318 | let y = *sig.next()? as u32; 319 | let z = *sig.next()? as u32; 320 | let u = *sig.next()? as u32; 321 | Some(((x & 0b0001_1111) << 24) + (y << 16) + (z << 8) + u) 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /src/metadata/token.rs: -------------------------------------------------------------------------------- 1 | use super::metadata::*; 2 | 3 | /// Represent a metadata token 4 | #[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)] 5 | pub struct Token(pub u32); 6 | 7 | /// ``DecodedToken(table, entry)``. ``entry`` is 1-based index. 8 | #[derive(Debug, Clone, PartialEq, Eq, Copy)] 9 | pub struct DecodedToken(pub u32, pub u32); 10 | 11 | impl Into for u16 { 12 | fn into(self) -> Token { 13 | Token(self as u32) 14 | } 15 | } 16 | 17 | impl Into for u32 { 18 | fn into(self) -> Token { 19 | Token(self) 20 | } 21 | } 22 | 23 | impl Into for DecodedToken { 24 | fn into(self) -> Token { 25 | let DecodedToken(table, entry) = self; 26 | encode_token(table, entry) 27 | } 28 | } 29 | 30 | pub fn decode_resolution_scope_token>(token: T) -> DecodedToken { 31 | let Token(raw_token) = token.into(); 32 | let tag = raw_token & 0b11; 33 | let table: u32 = match tag { 34 | 0 => TableKind::Module, 35 | 1 => TableKind::ModuleRef, 36 | 2 => TableKind::AssemblyRef, 37 | 3 => TableKind::TypeRef, 38 | _ => unreachable!(), 39 | } 40 | .into(); 41 | let entry = raw_token >> 2; 42 | DecodedToken(table, entry) 43 | } 44 | 45 | pub fn decode_typedef_or_ref_token>(token: T) -> DecodedToken { 46 | let Token(raw_token) = token.into(); 47 | let tag = raw_token & 0b11; 48 | let idx = raw_token >> 2; 49 | DecodedToken( 50 | match tag { 51 | 0 => TableKind::TypeDef.into(), 52 | 1 => TableKind::TypeRef.into(), 53 | 2 => TableKind::TypeSpec.into(), 54 | _ => unreachable!(), 55 | }, 56 | idx, 57 | ) 58 | } 59 | 60 | pub fn decode_member_ref_parent_token>(token: T) -> DecodedToken { 61 | let Token(raw_token) = token.into(); 62 | let tag = raw_token & 0b111; 63 | let table: u32 = match tag { 64 | 0 => TableKind::TypeDef, 65 | 1 => TableKind::TypeRef, 66 | 2 => TableKind::ModuleRef, 67 | 3 => TableKind::MethodDef, 68 | 4 => TableKind::TypeSpec, 69 | _ => unreachable!(), 70 | } 71 | .into(); 72 | let entry = raw_token >> 3; 73 | DecodedToken(table, entry) 74 | } 75 | 76 | pub fn encode_token(table: u32, entry: u32) -> Token { 77 | Token((table << (32 - 8)) | entry) 78 | } 79 | 80 | pub fn decode_token>(token: T) -> DecodedToken { 81 | let Token(raw_token) = token.into(); 82 | let table = raw_token >> (32 - 8); 83 | let entry = raw_token & 0x00ff_ffff; 84 | DecodedToken(table, entry) 85 | } 86 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod name_path; 2 | pub mod resolver; 3 | -------------------------------------------------------------------------------- /src/util/name_path.rs: -------------------------------------------------------------------------------- 1 | use crate::metadata::class::ClassInfo; 2 | 3 | /// MethodPath. Use Vec<_> internally since classes can be nested. 4 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 5 | pub struct MethodPath<'a>(pub Vec<&'a str>); 6 | 7 | /// TypePath. Use Vec<_> internally since classes can be nested. 8 | #[derive(Debug, Clone, Hash, Eq, PartialEq)] 9 | pub struct TypePath<'a>(pub Vec<&'a str>); 10 | 11 | impl<'a> Into> for TypePath<'a> { 12 | fn into(self) -> Vec<&'a str> { 13 | self.0 14 | } 15 | } 16 | 17 | impl<'a> Into> for MethodPath<'a> { 18 | fn into(self) -> Vec<&'a str> { 19 | self.0 20 | } 21 | } 22 | 23 | impl<'a> Into> for &'a ClassInfo { 24 | fn into(self) -> TypePath<'a> { 25 | TypePath(vec![ 26 | self.resolution_scope.get_name(), 27 | self.namespace.as_str(), 28 | self.name.as_str(), 29 | ]) 30 | } 31 | } 32 | 33 | impl<'a> TypePath<'a> { 34 | pub fn with_method_name(self, name: &'a str) -> MethodPath<'a> { 35 | let TypePath(mut path) = self; 36 | path.push(name); 37 | MethodPath(path) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/util/resolver.rs: -------------------------------------------------------------------------------- 1 | use rustc_hash::FxHashMap; 2 | use std::fmt::Debug; 3 | 4 | #[derive(Debug, Clone)] 5 | pub enum TreeMap { 6 | Map(FxHashMap>), 7 | Value(T), 8 | } 9 | 10 | impl TreeMap { 11 | pub fn as_map(&self) -> Option<&FxHashMap>> { 12 | match self { 13 | TreeMap::Map(map) => Some(map), 14 | TreeMap::Value(_) => None, 15 | } 16 | } 17 | 18 | pub fn as_value(&self) -> Option<&T> { 19 | match self { 20 | TreeMap::Map(_) => None, 21 | TreeMap::Value(val) => Some(val), 22 | } 23 | } 24 | 25 | pub fn as_value_mut(&mut self) -> Option<&mut T> { 26 | match self { 27 | TreeMap::Map(_) => None, 28 | TreeMap::Value(val) => Some(val), 29 | } 30 | } 31 | 32 | pub fn contains_key<'a, P: Into>>(&self, path: P) -> bool { 33 | let mut cur = self; 34 | for name in path.into() { 35 | match cur { 36 | TreeMap::Map(map) => { 37 | if let Some(next) = map.get(name) { 38 | cur = next 39 | } else { 40 | return false; 41 | } 42 | } 43 | TreeMap::Value(_) => return false, 44 | } 45 | } 46 | matches!(cur, TreeMap::Value(_)) 47 | } 48 | 49 | pub fn get<'a, P: Into>>(&self, path: P) -> Option<&T> { 50 | let mut cur = self; 51 | for name in path.into() { 52 | match cur { 53 | TreeMap::Map(map) => cur = map.get(name)?, 54 | TreeMap::Value(_) => return None, 55 | } 56 | } 57 | cur.as_value() 58 | } 59 | 60 | pub fn get_mut<'a, P: Into>>(&mut self, path: P) -> Option<&mut T> { 61 | let mut cur = self; 62 | for name in path.into() { 63 | match cur { 64 | TreeMap::Map(map) => cur = map.get_mut(name)?, 65 | TreeMap::Value(_) => return None, 66 | } 67 | } 68 | cur.as_value_mut() 69 | } 70 | // 71 | // pub fn get_mut_or<'a, P: Into>>(&mut self, path: P, default: T) -> Option<&mut T> { 72 | // if !self.contains_key(path) { 73 | // self.add(path, default); 74 | // } 75 | // self.get_mut(path) 76 | // } 77 | 78 | pub fn add<'a, P: Into>>(&mut self, path_: P, val: T) -> Option<()> { 79 | let path = path_.into(); 80 | let len = path.len(); 81 | let mut cur = self; 82 | for (i, name) in path.iter().enumerate() { 83 | let last = i == len - 1; 84 | match cur { 85 | TreeMap::Map(ref mut map) if last => { 86 | map.insert(name.to_string(), TreeMap::Value(val)); 87 | break; 88 | } 89 | TreeMap::Map(ref mut map) => { 90 | cur = map 91 | .entry(name.to_string()) 92 | .or_insert_with(|| TreeMap::Map(FxHashMap::default())) 93 | } 94 | TreeMap::Value(_) => return None, 95 | } 96 | } 97 | Some(()) 98 | } 99 | 100 | pub fn collect_values(&self) -> Vec { 101 | let mut values = vec![]; 102 | match self { 103 | TreeMap::Map(map) => { 104 | for elem in map.values() { 105 | values.append(&mut elem.collect_values()) 106 | } 107 | } 108 | TreeMap::Value(val) => values.push(val.clone()), 109 | } 110 | values 111 | } 112 | } 113 | 114 | #[derive(Debug, Clone)] 115 | pub struct NameResolver { 116 | map: TreeMap, 117 | } 118 | 119 | impl NameResolver { 120 | pub fn new() -> Self { 121 | NameResolver { 122 | map: TreeMap::Map(FxHashMap::default()), 123 | } 124 | } 125 | 126 | pub fn get<'a, P: Into>>(&self, path: P) -> Option<&T> { 127 | self.map.get(path.into()) 128 | } 129 | 130 | pub fn get_mut<'a, P: Into>>(&mut self, path: P) -> Option<&mut T> { 131 | self.map.get_mut(path.into()) 132 | } 133 | 134 | pub fn add<'a, P: Into>>(&mut self, path: P, val: T) { 135 | self.map.add(path.into(), val).unwrap() 136 | } 137 | 138 | pub fn collect_values(&self) -> Vec { 139 | self.map.collect_values() 140 | } 141 | } 142 | --------------------------------------------------------------------------------