├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── PROPOSAL.md ├── README.md ├── REPORT.md ├── data ├── HelloWorld.class └── String.class ├── rt ├── HelloWorld.java ├── java │ ├── io │ │ ├── FilterOutputStream.java │ │ ├── IOException.java │ │ ├── OutputStream.java │ │ └── PrintStream.java │ └── lang │ │ ├── Class.java │ │ ├── Error.java │ │ ├── Exception.java │ │ ├── Object.java │ │ ├── Override.java │ │ ├── RuntimeException.java │ │ ├── String.java │ │ ├── System.java │ │ ├── Throwable.java │ │ └── annotation │ │ └── Annotation.java └── moon │ └── RustStdout.java ├── src ├── bin │ └── main.rs ├── lib.rs ├── logging.rs ├── model │ ├── class_file │ │ ├── access_flags.rs │ │ ├── attribute │ │ │ ├── annotation.rs │ │ │ └── mod.rs │ │ ├── constant_pool.rs │ │ └── mod.rs │ └── mod.rs ├── parser │ ├── class_file.rs │ ├── mod.rs │ └── nom_support.rs ├── util │ ├── mod.rs │ ├── modified_utf8.rs │ └── one_indexed_vec.rs └── vm │ ├── bytecode.rs │ ├── class.rs │ ├── class_loader.rs │ ├── constant_pool.rs │ ├── frame.rs │ ├── mod.rs │ ├── native.rs │ └── value.rs └── tests ├── hello_world.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | script: "cargo build --verbose" 3 | after_success: | 4 | [ $TRAVIS_BRANCH = master ] && 5 | [ $TRAVIS_PULL_REQUEST = false ] && 6 | cargo rustdoc --lib -- --no-defaults --passes collapse-docs --passes unindent-comments --passes strip-priv-imports && 7 | echo "" > target/doc/index.html && 8 | sudo pip install ghp-import && 9 | ghp-import -n target/doc && 10 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 11 | env: 12 | global: 13 | secure: JlkLYvU3ES7d/YfvVc0lcwWxxXNfG03fCeDCK3tLoVXO/4aL4QlhpWW3VxKuKY5ghWP9F4kRY8c7zfRgyQzSE0zGapywtGSWFgiw2W9ytGrS3SkK62xqtmf6YkkbjGmK85DKUZ3NuOuAr53+NVk/FxS7c+5EBLoUIE6EtBUiZxXsWoMqkfsOSOJo18KRnSTcWTb6ajhd5Ufngx/Il0CcQHdWNMw9ZllpXMltygnOntQoDKuavhiMchTzm9IDe/U5Sd7WFyNRyJvAZJz67UbAO1LgAA2o24qGa6fKNnTZulZG8C+9nvsuyP7SfJYnh2L21KkHtzPqczIrBJROoPDmE4d1enQV8s6ekk4sRTI51P6RYr/5jpzT7J4PeSuAU46v9D2pfbytkJem1+FpZu2SByjy4VIAQbkhS8h0p/uiLqNRoX0YmhOqLoru93XPjk6oyQt7sRiPjBf4sqS+o5ebCbhrsl+pKeuAbnqVr5sSzhcPYYTNTxWsUgdfkopNpQfocOZlbExvj6b9ZH1d/OxtRfvgQM3UOQ6pPq6ErQWZiPPL+AmybTlfZxZp6qkvzd2VOhBB1DjGD3jp855gE3gj3A135TvBPsluxZTttHRPgtlDsvvgFCFz7owKcx5nK1jwO7m6GRYQofHDfuq4lDvJTgCph8s2c39raIeJ+5fNWqg= 14 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | [root] 2 | name = "rust-jvm" 3 | version = "0.1.0" 4 | dependencies = [ 5 | "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 6 | "nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 7 | ] 8 | 9 | [[package]] 10 | name = "log" 11 | version = "0.3.6" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | 14 | [[package]] 15 | name = "nom" 16 | version = "1.2.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-jvm" 3 | version = "0.1.0" 4 | authors = [ 5 | "Meyer Kizner ", 6 | "Max McCarthy ", 7 | "David Xu " 8 | ] 9 | documentation = "https://maxmcc.github.io/rust-jvm" 10 | 11 | [dependencies] 12 | log = "*" 13 | nom = "^1.2.2" 14 | -------------------------------------------------------------------------------- /PROPOSAL.md: -------------------------------------------------------------------------------- 1 | # rust-jvm [![Build Status](https://travis-ci.org/maxmcc/rust-jvm.svg?branch=master)](https://travis-ci.org/maxmcc/rust-jvm) 2 | CIS 198 Final Project 3 | Meyer Kizner, Max McCarthy, David Xu 4 | 5 | ## Abstract 6 | 7 | We propose to implement an interpreter for (a subset of) Java bytecode 8 | instructions in Rust. The intent is to allow us to execute simple Java .class 9 | files using Rust rather than C/C++, which is the implementation languages for 10 | most Java VMs. While our implementation will target a modest portion of the 11 | bytecode instructions (perhaps ignoring types other than `int`, `boolean`, and 12 | references), we hope it will demonstrate the usefulness of Rust for implementing 13 | language runtimes safely without significant performance loss. 14 | 15 | ## Licensing 16 | 17 | We intend to develop our VM as an open-source project under the Apache 2 license. 18 | 19 | ## Project Outline 20 | 21 | ### Minimal Goals 22 | Interpret a compiled Java `.class` file containing a simple program like hello world. This will require us to implement at least the following: 23 | 24 | - An interpreter for a minimal set of bytecode instructions necessary to 25 | execute the simple program 26 | - Managed heap allocation of objects, potentially with garbage collection (not 27 | sure yet) 28 | - Linking against the Java dynamic libraries so we can execute native Java 29 | calls (e.g., writing bytes to a file descriptor), or perhaps writing some of 30 | those in Rust instead 31 | 32 | ### Expected Goals 33 | Interpret generated bytecode for Java programs of reasonable complexity, including dynamic dispatch and object inheritance, multiple files. 34 | 35 | ### Stretch Goals 36 | Implement remaining bytecode instructions, perhaps extending our implementation to include types other than int, bool, and references. Implement support for exception handling. Optimize interpreter and garbage collector, implement JIT compilation, add support for concurrency and monitors, ... 37 | 38 | 39 | ## Tentative Schedule 40 | 41 | ### Week 1 42 | Investigate possible architectures for the VM, define the subset of bytecode 43 | instructions we want to support, probably by writing different Java programs and 44 | inspecting the `.class` files they produce. Implement features for opening, 45 | reading, and parsing `.class` files into a useable format. 46 | 47 | ### Week 2 48 | Begin work on dynamically linking existing Java libraries into the VM so library 49 | functions can be called. Implement stack and heap components, implement GC (or 50 | find alternative solution to memory management—maybe just leaking allocated 51 | memory?). Implement and unit-test stack manipulation instructions. 52 | 53 | ### Week 3 54 | Complete outstanding work from previous two weeks, or continue with stretch 55 | goals. Continue testing and debugging, especially with `javac` output. Ensure 56 | documentation is thorough. 57 | 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-jvm 2 | 3 | This project was submitted by Meyer Kizner, Max McCarthy, and David Xu as our 4 | final project for [CIS 198](//cis198-2016s.github.io) at the University of 5 | Pennsylvania in spring 2016. 6 | 7 | Please note that because this project was completed for a course, it is **not** 8 | under active development. The code and documentation in this repository is 9 | available as-is, and we will not be providing support beyond what already exists 10 | here. 11 | 12 | ## About 13 | 14 | This project implements a considerable subset of a Java virtual machine, in 15 | accordance with the Java SE 7 specification. It consists of a parser, class 16 | loader, and interpreter, which together can be used to read an arbitrary class 17 | file (subject to certain restrictions on the Java bytecode it contains), load it 18 | (and other necessary classes from the Java runtime libraries) into an 19 | interpreter, and execute the `main` method. 20 | 21 | We achieved near-completion of many of these features, but got stuck within 22 | initialization of the `PrintWriter` and `FileDescriptor` classes for the 23 | `System.out.println` method, since they used a number of JVM features (namely, 24 | loading the `java.util.concurrent.atomic.AtomicInteger` class) which we had not 25 | intended to support. For this reason, the minimum required parts of the Java 26 | Class Library (JCL) are reimplemented in the `rt/` directory in terms of a 27 | `RustStdout` class, which calls a `native` method for writing to a file 28 | descriptor. 29 | 30 | As it stands, the VM successfully executes a `HelloWorld.class` file. It has not 31 | been tested beyond that. 32 | 33 | To find out more information about the project, please see 34 | [PROPOSAL.md](//github.com/maxmcc/rust-jvm/blob/master/PROPOSAL.md) and 35 | [REPORT.md](//github.com/maxmcc/rust-jvm/blob/master/REPORT.md) for our initial 36 | project propsoal and final report. 37 | 38 | ## Running Hello World 39 | 40 | This project is organized around a single integration test: executing a compiled 41 | "Hello, world!" program from a `.class` file. 42 | 43 | Our VM does not support Java 7 and 8 features (including lambdas and the 44 | `invokedynamic` instruction). You will need to compile the Java source code in 45 | the `rt` directory targeting source level `1.6`, which can be accomplished with 46 | the following command: 47 | 48 | ```sh 49 | $ cd rt/ 50 | $ javac -source 1.6 -target 1.6 HelloWorld.java 51 | ``` 52 | 53 | Then, run `cargo test` from the project root and you should see the test pass. 54 | 55 | ## License 56 | 57 | This software is freely available under the terms of the [Apache License 58 | 2.0](//apache.org/licenses/LICENSE-2.0). 59 | 60 | -------------------------------------------------------------------------------- /REPORT.md: -------------------------------------------------------------------------------- 1 | # Rust JVM Project Final Report 2 | CIS 198 Final Project 3 | Meyer Kizner, Max McCarthy, David Xu 4 | 5 | ## Summary 6 | 7 | We implemented a considerable subset of a Java virtual machine, in accordance 8 | with the Java SE 7 specification for the JVM. Our project consists of a parser, 9 | class loader, and interpreter, which together can be used to read an arbitrary 10 | class file (subject to certain restrictions on the Java bytecode it contains), 11 | load it (and other necessary classes from the Java runtime libraries) into an 12 | interpreter, and execute the main method. We achieved near-completion of each of 13 | these features, but got stuck within initialization of the `PrintWriter` and 14 | `FileDescriptor` classes for the `System.out.println` method, since they used a 15 | number of JVM features (namely, loading the 16 | `java.util.concurrent.atomic.AtomicInteger` class) which we had not intended to 17 | support and did not end up having time to complete. 18 | 19 | ## Approximate time spent 20 | 21 | We devoted approximately 120 person-hours to this project. 22 | 23 | ## Project structure 24 | 25 | To aid in our development of the class file parser, we relied on a (somewhat 26 | immature) parser combinator library called [nom](//github.com/Geal/nom). 27 | Unfortunately, nom's error model produces pretty much unuseable error messages, 28 | so much of our effort in developing the parser was spent on wrapping nom's 29 | macros with other macros to provide backtracking capabilities. 30 | 31 | The remainder of our project consists of the `vm` module, which consists of a 32 | bootstrap class loader, type definitions for various runtime entities like 33 | classes, objects, methods, and arrays, and the implementation of the JVM stack, 34 | its frames, and a (pretty big!) pattern-match on various bytecode instructions. 35 | 36 | We found that despite the JVM specification being a reasonably thorough 550-page 37 | document, significant portions of key functionality were underspecified (or 38 | clearly written under many of the same assumptions made by Oracle's existing 39 | HotSpot JVM implementation). At many points, we found we had to make design 40 | choices because Oracle derived their specification a C++ reference 41 | implementation, which precluded us from using Rust's more modern language 42 | features in several key areas. For instance, we had intended to translate the 43 | individual bytecode instructions into more semantic cases of a Rust enum, 44 | combining things like `iload_0`, `iload_1`, `iload_2`, etc. into a single 45 | `iload` instruction. However, the addition of a `wide` operand made this 46 | approach untenable, because it required a reinterpretation of the bytes in the 47 | code section of a method. 48 | 49 | Another particularly frustrating design decision made by Sun/Oracle was to have 50 | 32-bit int, float, and reference values occupy one index of the constant pool, 51 | while 64-bit long and double values occupy two indices. Following Oracle's lead 52 | would require us to break up these larger values to store them in an array of 53 | Rust type `Vec`, and to recombine them whenever necessary to use Rust's i64 54 | and f64 operations. We broke with Oracle's largely untyped approach to their JVM 55 | implementation by introducing an enum called `Value` with a tag for each type, 56 | and modeling the constant pool array as a `Vec>`. We push either a 57 | single 64-bit value or a 32-bit value followed by `None` so our indices match 58 | the ones appearing in the bytecode instructions themselves. (Oracle did note in 59 | a footnote that, "In retrospect, making 8-byte constants take two constant pool 60 | entries was a poor choice," which was at least somewhat gratifying to read.) 61 | 62 | ## Testing 63 | 64 | We expect our project to be unlike the majority of others submitted for this 65 | class in that our implementation was almost completely driven by a single 66 | integration test---the HelloWorld class. Because the architecture of the JVM 67 | as-specified is so tightly-coupled, it would have been difficult to modularize 68 | and unit-test parts of our implementation while remaining largely 69 | specification-compliant. (We did some amount of unit-testing for our parser.) At 70 | many times, we also created modified or bogus class files by hand and ran them 71 | in the HotSpot JVM to observe its behavior where the specification was unclear 72 | or ambiguous. 73 | 74 | ## Benchmarks 75 | 76 | We didn't bother with any rigorous benchmarking of our JVM versus any existing 77 | implementation of the JVM specification, for several pretty obvious reasons. For 78 | instance, we don't support much more than the minimum instructions necessary to 79 | implement a HelloWorld program. Also, our JVM also doesn't perform just-in-time 80 | lowering of bytecode to native code. However, we do achieve a slight performance 81 | advantage by not bothering to garbage-collect anything! 82 | 83 | ## Limitations 84 | 85 | The primary limitation of our program is its lack of support for arbitrary 86 | programs, especially arbitrary uses of the Java standard library implementation. 87 | Oracle’s implementation of the Java class libraries makes extensive internal use 88 | of the undocumented sun package, which itself calls many native methods 89 | implemented in C++ with respect to Oracle’s JVM implementation. In order to use 90 | arbitrary Java classes, we would need to write versions of the class library 91 | that are implemented with our own Rust “native” methods. 92 | 93 | There are many JVM features which we did not attempt to implement at all: among 94 | others, we are missing multithreading and concurrency; reflection; the 95 | invokedynamic instruction in Java 7 and the features introduced in Java 8; 96 | garbage collection; inner classes (other than in the parser); enums; exceptions 97 | and exception handling; custom class loaders; and bytecode verification. 98 | 99 | ## What went well 100 | 101 | Our class file parser is more or less feature-complete, and capable of parsing 102 | Java class files including all features up to and including Java 8. (Our VM does 103 | not attempt to implement some Java 7 and 8 features, however.) The output of the 104 | parser is a Rust structure which allows the class loader to fairly easily create 105 | the values needed to populate the runtime representation of a class. 106 | 107 | We put extensive effort into modeling the classes, methods, and other structures 108 | needed to represent the state of the JVM. Many of the error conditions in the 109 | specification are checked for as specified, although since we do not implement 110 | exceptions or exception handling, we simply panic! when one of these internal 111 | `java.lang.Error` conditions arise. With an implementation of the Java class 112 | library which is compatible with our JVM, we believe it would be possible to run 113 | many single-threaded Java programs (although not all instructions are 114 | implemented). 115 | 116 | ## What we would do differently 117 | 118 | Given the time invested in fixing the parser's error handling, it might have 119 | been a better investment to write the class file parser by hand. That said, we 120 | hope we to contribute our work back to the nom project as an [example parser]( 121 | https://github.com/Geal/nom/issues/14). 122 | 123 | We expect that had each of us had more time to understand the specification and 124 | Oracle's design decisions, we would have been able to complete the 125 | implementation of a substantially larger amount of the JVM features. Perhaps the 126 | choice of the JVM was too ambitious---it might have been a better idea to choose 127 | a smaller and better-designed language bytecode (such as the OCaml bytecode). 128 | That said, we learned a lot about the JVM internals as well as about Rust's 129 | various language features in completing this project, and are satisfied with our 130 | progress even if the demo isn't quite what we had hoped for in the beginning. 131 | 132 | -------------------------------------------------------------------------------- /data/HelloWorld.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmcc/rust-jvm/af1ddffa6f04ef7deaf1f36f9acf3599a690ff87/data/HelloWorld.class -------------------------------------------------------------------------------- /data/String.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxmcc/rust-jvm/af1ddffa6f04ef7deaf1f36f9acf3599a690ff87/data/String.class -------------------------------------------------------------------------------- /rt/HelloWorld.java: -------------------------------------------------------------------------------- 1 | final class HelloWorld { 2 | public static void main(String[] args) { 3 | System.out.println("Hello, world!"); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /rt/java/io/FilterOutputStream.java: -------------------------------------------------------------------------------- 1 | package java.io; 2 | 3 | public class FilterOutputStream extends OutputStream { 4 | protected OutputStream out; 5 | 6 | public FilterOutputStream(OutputStream out) { 7 | this.out = out; 8 | } 9 | 10 | @Override 11 | public void write(int b) throws IOException { 12 | out.write(b); 13 | } 14 | 15 | @Override 16 | public void write(byte[] b) throws IOException { 17 | write(b, 0, b.length); 18 | } 19 | 20 | @Override 21 | public void write(byte[] b, int off, int len) throws IOException { 22 | for (int i = 0; i < len; i++) { 23 | write(b[off + i]); 24 | } 25 | } 26 | 27 | @Override 28 | public void flush() throws IOException { 29 | out.flush(); 30 | } 31 | 32 | @Override 33 | public void close() throws IOException { 34 | out.close(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /rt/java/io/IOException.java: -------------------------------------------------------------------------------- 1 | package java.io; 2 | 3 | public class IOException extends Exception { 4 | public IOException() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rt/java/io/OutputStream.java: -------------------------------------------------------------------------------- 1 | package java.io; 2 | 3 | public abstract class OutputStream { 4 | public OutputStream() { 5 | } 6 | 7 | public abstract void write(int b) throws IOException; 8 | 9 | public void write(byte[] b) throws IOException { 10 | write(b, 0, b.length); 11 | } 12 | 13 | public void write(byte[] b, int off, int len) throws IOException { 14 | for (int i = 0; i < len; i++) { 15 | write(b[off + i]); 16 | } 17 | } 18 | 19 | public void flush() throws IOException { 20 | } 21 | 22 | public void close() throws IOException { 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /rt/java/io/PrintStream.java: -------------------------------------------------------------------------------- 1 | package java.io; 2 | 3 | // TODO can you make one of these without automatic flushing? 4 | // TODO InterruptedIOException (see checkError() docs) 5 | public class PrintStream extends FilterOutputStream { 6 | private boolean error; 7 | 8 | public PrintStream(OutputStream out) { 9 | super(out); 10 | error = false; 11 | } 12 | 13 | @Override 14 | public void flush() { 15 | try { 16 | super.flush(); 17 | } catch (IOException iox) { 18 | error = true; 19 | } 20 | } 21 | 22 | @Override 23 | public void close() { 24 | try { 25 | flush(); 26 | super.close(); 27 | } catch (IOException iox) { 28 | error = true; 29 | } 30 | } 31 | 32 | public boolean checkError() { 33 | return error; 34 | } 35 | 36 | protected void setError() { 37 | error = true; 38 | } 39 | 40 | protected void clearError() { 41 | error = false; 42 | } 43 | 44 | @Override 45 | public void write(int b) { 46 | try { 47 | super.write(b); 48 | if (b == '\n') { 49 | flush(); 50 | } 51 | } catch (IOException iox) { 52 | error = true; 53 | } 54 | } 55 | 56 | @Override 57 | public void write(byte[] b, int off, int len) { 58 | try { 59 | super.write(b, off, len); 60 | flush(); 61 | } catch (IOException iox) { 62 | error = true; 63 | } 64 | } 65 | 66 | public void print(String s) { 67 | byte[] b; 68 | if (s == null) { 69 | b = "null".getBytes(); 70 | } else { 71 | b = s.getBytes(); 72 | } 73 | write(b, 0, b.length); 74 | } 75 | 76 | public void println() { 77 | // TODO because fuck portability 78 | write('\n'); 79 | } 80 | 81 | public void println(String x) { 82 | print(x); 83 | println(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /rt/java/lang/Class.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public final class Class { 4 | private Class() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rt/java/lang/Error.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public class Error extends Throwable { 4 | public Error() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rt/java/lang/Exception.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public class Exception extends Throwable { 4 | public Exception() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rt/java/lang/Object.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public class Object { 4 | public Object() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rt/java/lang/Override.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public @interface Override { 4 | } 5 | -------------------------------------------------------------------------------- /rt/java/lang/RuntimeException.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public class RuntimeException extends Exception { 4 | public RuntimeException() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rt/java/lang/String.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public final class String { 4 | private final char[] value; 5 | 6 | public String(char[] value) { 7 | this.value = new char[value.length]; 8 | System.arraycopy(value, 0, this.value, 0, value.length); 9 | } 10 | 11 | public byte[] getBytes() { 12 | // TODO we're assuming UTF-16 here 13 | byte[] b = new byte[2 * value.length]; 14 | for (int i = 0; i < value.length; i++) { 15 | b[2 * i] = (byte) ((value[i] & 0xff00) >>> 8); 16 | b[2 * i + 1] = (byte) (value[i] & 0x00ff); 17 | } 18 | return b; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /rt/java/lang/System.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | import java.io.PrintStream; 4 | 5 | import moon.RustStdout; 6 | 7 | public final class System { 8 | public static PrintStream out = new PrintStream(new RustStdout()); 9 | 10 | private static native PrintStream setStdout(); 11 | 12 | public static native void arraycopy(Object src, int srcPos, Object dest, 13 | int destPos, int length); 14 | 15 | private System() { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /rt/java/lang/Throwable.java: -------------------------------------------------------------------------------- 1 | package java.lang; 2 | 3 | public class Throwable { 4 | public Throwable() { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /rt/java/lang/annotation/Annotation.java: -------------------------------------------------------------------------------- 1 | package java.lang.annotation; 2 | 3 | public interface Annotation { 4 | boolean equals(Object obj); 5 | 6 | int hashCode(); 7 | 8 | String toString(); 9 | 10 | Class annotationType(); 11 | } 12 | -------------------------------------------------------------------------------- /rt/moon/RustStdout.java: -------------------------------------------------------------------------------- 1 | package moon; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | public final class RustStdout extends OutputStream { 7 | public RustStdout() { 8 | } 9 | 10 | @Override 11 | public void write(int b) throws IOException { 12 | byte[] arr = new byte[1]; 13 | arr[0] = (byte) b; 14 | write(arr); 15 | } 16 | 17 | @Override 18 | public native void write(byte[] b, int off, int len) throws IOException; 19 | } 20 | -------------------------------------------------------------------------------- /src/bin/main.rs: -------------------------------------------------------------------------------- 1 | extern crate rust_jvm; 2 | 3 | use std::io::Read; 4 | 5 | use rust_jvm::parser::class_file; 6 | use rust_jvm::logging::SimpleLogger; 7 | 8 | fn main() { 9 | SimpleLogger::init().unwrap(); 10 | let file_name = std::env::args().nth(1).unwrap(); 11 | let mut file = std::fs::File::open(file_name).unwrap(); 12 | let mut bytes = vec![]; 13 | file.read_to_end(&mut bytes).unwrap(); 14 | let class = class_file::parse_class_file(&bytes); 15 | println!("{:#?}", class); 16 | } 17 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "https://maxmcc.github.io/rust-jvm/")] 2 | 3 | //! This is a documentation comment for our library. 4 | 5 | #[macro_use] 6 | extern crate log; 7 | 8 | #[macro_use] 9 | extern crate nom; 10 | 11 | pub mod logging; 12 | pub mod model; 13 | pub mod parser; 14 | pub mod util; 15 | pub mod vm; 16 | -------------------------------------------------------------------------------- /src/logging.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use log; 4 | use log::{LogRecord, LogLevelFilter, LogMetadata, SetLoggerError}; 5 | 6 | /// Prints to standard error. 7 | #[macro_export] 8 | macro_rules! eprintln { 9 | ($($arg:tt)*) => ({ 10 | writeln!(&mut ::std::io::stderr(), $($arg)*).unwrap() 11 | }) 12 | } 13 | 14 | #[macro_export] 15 | macro_rules! with_warn { 16 | ($expr: expr) => (with_warn!("{}", $expr)); 17 | ($fmt: tt, $expr: expr) => (match $expr { 18 | Ok(v) => v, 19 | Err(e) => warn!($fmt, e), 20 | }); 21 | } 22 | 23 | #[macro_export] 24 | macro_rules! catching { 25 | ($expr: expr) => (catching!("{:?}", $expr)); 26 | ($fmt: tt, $expr: expr) => (match $expr { 27 | Ok(v) => v, 28 | Err(e) => debug!($fmt, e), 29 | }); 30 | } 31 | 32 | #[macro_export] 33 | macro_rules! catching_at { 34 | ($log_fn: ident, $expr: expr) => ($log_fn!("{:?}", $expr)); 35 | ($log_fn: ident, $fmt: tt, $expr: expr) => (match $expr { 36 | Ok(v) => v, 37 | Err(e) => $log_fn!($fmt, e), 38 | }); 39 | } 40 | 41 | 42 | const MAX_LOG_LEVEL: LogLevelFilter = LogLevelFilter::Debug; 43 | 44 | pub struct SimpleLogger; 45 | 46 | impl SimpleLogger { 47 | pub fn init() -> Result<(), SetLoggerError> { 48 | log::set_logger(|max_log_level| { 49 | max_log_level.set(MAX_LOG_LEVEL); 50 | Box::new(SimpleLogger) 51 | }) 52 | } 53 | } 54 | 55 | impl log::Log for SimpleLogger { 56 | 57 | fn enabled(&self, _: &LogMetadata) -> bool { 58 | true 59 | } 60 | 61 | fn log(&self, record: &LogRecord) { 62 | if self.enabled(record.metadata()) { 63 | eprintln!("[{}] [{}] {}", record.metadata().target(), record.level(), record.args()); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/model/class_file/access_flags.rs: -------------------------------------------------------------------------------- 1 | //! Access flags for various structures in 2 | //! [§4.1](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1). 3 | 4 | #[allow(non_camel_case_types)] 5 | pub type u2 = super::u2; 6 | 7 | /// Values of access flags for a class or interface. 8 | pub mod class_access_flags { 9 | #[allow(non_camel_case_types)] 10 | pub type access_flag = super::u2; 11 | #[allow(non_camel_case_types)] 12 | pub type t = access_flag; 13 | 14 | /// Declared `public`; may be accessed from outside its package. 15 | pub const ACC_PUBLIC: t = 0x0001; 16 | /// Declared `final`; no subclasses allowed. 17 | pub const ACC_FINAL: t = 0x0010; 18 | /// Treat superclass methods specially when invoked by the _invokespecial_ 19 | /// instruction. 20 | pub const ACC_SUPER: t = 0x0020; 21 | /// Is an interface, not a class. 22 | pub const ACC_INTERFACE: t = 0x0200; 23 | /// Declared `abstract`; must not be instantiated. 24 | pub const ACC_ABSTRACT: t = 0x0400; 25 | /// Declared synthetic; not present in the source code. 26 | pub const ACC_SYNTHETIC: t = 0x1000; 27 | /// Declared as an annotation type. 28 | pub const ACC_ANNOTATION: t = 0x2000; 29 | /// Declared as an `enum` type. 30 | pub const ACC_ENUM: t = 0x4000; 31 | } 32 | 33 | /// Values of access flags for an inner class. 34 | pub mod inner_class_access_flags { 35 | #[allow(non_camel_case_types)] 36 | pub type access_flag = super::u2; 37 | #[allow(non_camel_case_types)] 38 | pub type t = access_flag; 39 | 40 | /// Marked or implicitly `public` in source. 41 | pub const ACC_PUBLIC: t = 0x0001; 42 | /// Marked `private` in source. 43 | pub const ACC_PRIVATE: t = 0x0002; 44 | /// Marked `protected` in source. 45 | pub const ACC_PROTECTED: t = 0x0004; 46 | /// Marked or implicitly `static` in source. 47 | pub const ACC_STATIC: t = 0x0008; 48 | /// Marked `final` in source. 49 | pub const ACC_FINAL: t = 0x0010; 50 | /// Was an `interface` in source. 51 | pub const ACC_INTERFACE: t = 0x0200; 52 | /// Marked or implicitly `abstract` in source. 53 | pub const ACC_ABSTRACT: t = 0x0400; 54 | /// Declared synthetic; not present in the source code. 55 | pub const ACC_SYNTHETIC: t = 0x1000; 56 | /// Declared as an annotation type. 57 | pub const ACC_ANNOTATION: t = 0x2000; 58 | /// Declared as an `enum` type. 59 | pub const ACC_ENUM: t = 0x4000; 60 | } 61 | 62 | /// Values of access flags for a field. 63 | pub mod field_access_flags { 64 | #[allow(non_camel_case_types)] 65 | pub type access_flag = super::u2; 66 | #[allow(non_camel_case_types)] 67 | pub type t = access_flag; 68 | 69 | /// Declared `public`; may be accessed from outside its package. 70 | pub const ACC_PUBLIC: t = 0x0001; 71 | /// Declared `private`; usable only within the defining class. 72 | pub const ACC_PRIVATE: t = 0x0002; 73 | /// Declared `protected`; may be accessed within subclasses. 74 | pub const ACC_PROTECTED: t = 0x0004; 75 | /// Declared `static`. 76 | pub const ACC_STATIC: t = 0x0008; 77 | /// Declared `final`; no subclasses allowed. 78 | pub const ACC_FINAL: t = 0x0010; 79 | /// Declared `volatile`; cannot be cached. 80 | pub const ACC_VOLATILE: t = 0x0040; 81 | /// Declared `transient`; not written or read by a persistent object 82 | /// manager. 83 | pub const ACC_TRANSIENT: t = 0x0080; 84 | /// Declared synthetic; not present in the source code. 85 | pub const ACC_SYNTHETIC: t = 0x1000; 86 | /// Declared as an element of an `enum`. 87 | pub const ACC_ENUM: t = 0x4000; 88 | } 89 | 90 | /// Values of access flags for a method. 91 | pub mod method_access_flags { 92 | #[allow(non_camel_case_types)] 93 | pub type access_flag = super::u2; 94 | #[allow(non_camel_case_types)] 95 | pub type t = access_flag; 96 | 97 | /// Declared `public`; may be accessed from outside its package. 98 | pub const ACC_PUBLIC: t = 0x0001; 99 | /// Declared `private`; usable only within the defining class. 100 | pub const ACC_PRIVATE: t = 0x0002; 101 | /// Declared `protected`; may be accessed within subclasses. 102 | pub const ACC_PROTECTED: t = 0x0004; 103 | /// Declared `static`. 104 | pub const ACC_STATIC: t = 0x0008; 105 | /// Declared `final`; must not be overriden. 106 | pub const ACC_FINAL: t = 0x0010; 107 | /// Declared `synchronized`; invocation is wrapped by a monitor use. 108 | pub const ACC_SYNCHRONIZED: t = 0x0020; 109 | /// A bridge method, generated by the compiler. 110 | pub const ACC_BRIDGE: t = 0x0040; 111 | /// Declared with variable number of arguments. 112 | pub const ACC_VARARGS: t = 0x0080; 113 | /// Declared `native`; implemented in a language other than Java. 114 | pub const ACC_NATIVE: t = 0x0100; 115 | /// Declared `abstract`; no implementation is provided. 116 | pub const ACC_ABSTRACT: t = 0x0400; 117 | /// Declared `strictfp`; floating-point mode is FP-strict. 118 | pub const ACC_STRICT: t = 0x0800; 119 | /// Declared synthetic; not present in the source code. 120 | pub const ACC_SYNTHETIC: t = 0x1000; 121 | } 122 | 123 | /// Values of access flags for parameters. 124 | pub mod parameter_access_flags { 125 | #[allow(non_camel_case_types)] 126 | pub type access_flag = super::u2; 127 | #[allow(non_camel_case_types)] 128 | pub type t = access_flag; 129 | 130 | /// Indicates that the formal parameter was declared final. 131 | pub const ACC_FINAL: t = 0x0010; 132 | /// Indicates that the formal parameter was not explicitly or implicitly 133 | /// declared in source code, according to the specification of the language 134 | /// in which the source code was written (JLS §13.1). (The formal parameter 135 | /// is an implementation artifact of the compiler which produced this class 136 | /// file.) 137 | pub const ACC_SYNTHETIC: t = 0x1000; 138 | /// Indicates that the formal parameter was implicitly declared in source 139 | /// code, according to the specification of the language in which the source 140 | /// code was written (JLS §13.1). (The formal parameter is mandated by a 141 | /// language specification, so all compilers for the language must emit it.) 142 | pub const ACC_MANDATED: t = 0x8000; 143 | } 144 | -------------------------------------------------------------------------------- /src/model/class_file/attribute/annotation.rs: -------------------------------------------------------------------------------- 1 | use model::class_file::{constant_pool_index, u1, u2}; 2 | 3 | pub use self::element_value::ElementValue; 4 | pub use self::target_type::TargetInfo; 5 | 6 | pub mod element_value { 7 | use model::class_file::{constant_pool_index, u1}; 8 | 9 | #[derive(Debug)] 10 | pub enum ElementValue { 11 | Byte { const_value_index: constant_pool_index }, 12 | Char { const_value_index: constant_pool_index }, 13 | Double { const_value_index: constant_pool_index }, 14 | Float { const_value_index: constant_pool_index }, 15 | Int { const_value_index: constant_pool_index }, 16 | Long { const_value_index: constant_pool_index }, 17 | Short { const_value_index: constant_pool_index }, 18 | Boolean { const_value_index: constant_pool_index }, 19 | String { const_value_index: constant_pool_index }, 20 | Enum { type_name_index: constant_pool_index, const_name_index: constant_pool_index }, 21 | Class { class_info_index: constant_pool_index }, 22 | Annotation { annotation_value: super::Annotation }, 23 | Array { values: Vec }, 24 | } 25 | 26 | #[derive(Debug, PartialEq)] 27 | pub enum Tag { 28 | Byte, 29 | Char, 30 | Double, 31 | Float, 32 | Int, 33 | Long, 34 | Short, 35 | Boolean, 36 | String, 37 | Enum, 38 | Class, 39 | Annotation, 40 | Array, 41 | Unknown(u1), 42 | } 43 | impl From for Tag { 44 | fn from(tag: u1) -> Self { 45 | match tag { 46 | b'B' => Tag::Byte, 47 | b'C' => Tag::Char, 48 | b'D' => Tag::Double, 49 | b'F' => Tag::Float, 50 | b'I' => Tag::Int, 51 | b'J' => Tag::Long, 52 | b'S' => Tag::Short, 53 | b'Z' => Tag::Boolean, 54 | b's' => Tag::String, 55 | b'e' => Tag::Enum, 56 | b'c' => Tag::Class, 57 | b'@' => Tag::Annotation, 58 | b'[' => Tag::Array, 59 | tag => Tag::Unknown(tag), 60 | } 61 | } 62 | } 63 | } 64 | 65 | #[derive(Debug)] 66 | pub struct ElementValuePair { 67 | pub element_name_index: constant_pool_index, 68 | pub value: ElementValue, 69 | } 70 | 71 | #[derive(Debug)] 72 | pub struct LocalVariableTargetInfo { 73 | pub start_pc: u2, 74 | pub length: u2, 75 | pub index: u2, 76 | } 77 | 78 | pub mod target_type { 79 | use model::class_file::{u1, u2}; 80 | 81 | #[derive(Debug)] 82 | pub enum TargetInfo { 83 | TypeParameter { type_parameter_index: u1 }, 84 | Supertype { supertype_index: u2 }, 85 | TypeParameterBound { type_parameter_index: u1, bound_index: u1 }, 86 | Empty, 87 | FormalParameter { formal_parameter_index: u1 }, 88 | Throws { throws_type_index: u2 }, 89 | LocalVariable { table: Vec }, 90 | Catch { exception_table_index: u2 }, 91 | Offset { offset: u2 }, 92 | TypeArgument { offset: u2, type_argument_index: u1 }, 93 | } 94 | 95 | pub enum Tag { 96 | TypeParameter, 97 | Supertype, 98 | TypeParameterBound, 99 | Empty, 100 | FormalParameter, 101 | Throws, 102 | LocalVariable, 103 | Catch, 104 | Offset, 105 | TypeArgument, 106 | Unknown(u1), 107 | } 108 | 109 | impl From for Tag { 110 | fn from(tag: u1) -> Self { 111 | match tag { 112 | 0x00 | 0x01 => Tag::TypeParameter, 113 | 0x10 => Tag::Supertype, 114 | 0x11 | 0x12 => Tag::TypeParameterBound, 115 | 0x13 | 0x14 | 0x15 => Tag::Empty, 116 | 0x16 => Tag::FormalParameter, 117 | 0x17 => Tag::Throws, 118 | 119 | 0x40 | 0x41 => Tag::LocalVariable, 120 | 0x42 => Tag::Catch, 121 | 0x43 | 0x44 | 0x45 | 0x46 => Tag::Offset, 122 | 0x47 | 0x48 | 0x49 | 0x4A | 0x4B => Tag::TypeArgument, 123 | 124 | _ => Tag::Unknown(tag), 125 | } 126 | } 127 | } 128 | } 129 | 130 | #[derive(Debug)] 131 | pub struct Annotation { 132 | /// An index into the `constant_pool` table for a `ConstantPoolInfo::Utf8` structure. 133 | pub type_index: constant_pool_index, 134 | pub element_value_pairs: Vec, 135 | } 136 | 137 | #[derive(Debug)] 138 | pub struct TypePathPart { 139 | pub type_path_kind: u1, 140 | pub type_argument_index: u1, 141 | } 142 | 143 | #[derive(Debug)] 144 | pub struct TypePath { 145 | pub path: Vec, 146 | } 147 | 148 | #[derive(Debug)] 149 | pub struct TypeAnnotation { 150 | pub target_info: TargetInfo, 151 | pub target_path: TypePath, 152 | pub type_index: u2, 153 | pub element_value_pairs: Vec, 154 | } 155 | -------------------------------------------------------------------------------- /src/model/class_file/attribute/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod annotation; 2 | 3 | use super::u1; 4 | use super::u2; 5 | use super::constant_pool_index; 6 | use super::access_flags::inner_class_access_flags; 7 | use super::access_flags::parameter_access_flags; 8 | 9 | pub use self::stack_map_frame::StackMapFrame; 10 | 11 | /// Each `ExceptionTableEntry` describes one exception handler in the `code` 12 | /// array. The order of the handlers in an `exception_table` array is 13 | /// significant (§2.10). 14 | #[derive(Debug)] 15 | pub struct ExceptionTableEntry { 16 | /// Indicates the (inclusive) start of the range in the `code` array at 17 | /// which the exception handler is active. The value of `start_pc` must be a 18 | /// valid index into the `code` array of the opcode of an instruction. The 19 | /// exception handler is active in the range `[start_pc, end_pc)`. 20 | pub start_pc: u2, 21 | /// Indicates the (exclusive) end of the range in the `code` array at which 22 | /// the exception handler is active. The value of `end_pc` must be a valid 23 | /// index into the `code` array of the opcode of an instruction or must be 24 | /// equal to the length of the `code` array. The exception handler is active 25 | /// in the range `[start_pc, end_pc)`. 26 | pub end_pc: u2, 27 | /// The value of the `handler_pc` item indicates the start of the exception 28 | /// handler. The value of the item must be a valid index into the code array 29 | /// and must be the index of the opcode of an instruction. 30 | pub handler_pc: u2, 31 | /// If the value of the `catch_type` item is nonzero, it must be a valid 32 | /// index into the `constant_pool` table. The `constant_pool` entry at that 33 | /// index must be a `ConstantPoolInfo::Class` structure representing a class 34 | /// of exceptions that this exception handler is designated to catch. The 35 | /// exception handler will be called only if the thrown exception is an 36 | /// instance of the given class or one of its subclasses. 37 | pub catch_type: constant_pool_index, 38 | } 39 | 40 | pub mod stack_map_frame { 41 | use super::super::u1; 42 | use super::super::u2; 43 | 44 | pub use self::verification_type_info::VerificationTypeInfo; 45 | 46 | /// A `StackMapFrame` variant stores a relative bytecode offset, the 47 | /// verification types (§4.10.1.2) for the local variables, and the verification 48 | /// types for the operand stack. Each variant stores a bytecode offset _relative 49 | /// to the previous_ `StackMapFrame`. The actual bytecode offset can be 50 | /// calculated as described in (§4.7.4). 51 | #[derive(Debug)] 52 | pub enum StackMapFrame { 53 | SameFrame { offset_delta: u1 }, 54 | SameLocals1StackItemFrame { offset_delta: u1, stack_item: VerificationTypeInfo }, 55 | SameLocals1StackItemFrameExtended { offset_delta: u2, stack_item: VerificationTypeInfo }, 56 | ChopFrame { offset_delta: u2, num_chopped: u1 }, 57 | SameFrameExtended { offset_delta: u2 }, 58 | AppendFrame { offset_delta: u2, locals: Vec }, 59 | FullFrame { 60 | offset_delta: u2, 61 | locals: Vec, 62 | stack: Vec 63 | }, 64 | } 65 | 66 | #[derive(Debug, PartialEq)] 67 | pub enum Tag { 68 | SameFrame(u1), 69 | SameLocals1StackItemFrame(u1), 70 | SameLocals1StackItemFrameExtended(u1), 71 | ChopFrame(u1), 72 | SameFrameExtended(u1), 73 | AppendFrame(u1), 74 | FullFrame(u1), 75 | Reserved(u1), 76 | Unknown(u1), 77 | } 78 | 79 | impl From for Tag { 80 | fn from(t: u1) -> Self { 81 | match t { 82 | 0...63 => Tag::SameFrame(t), 83 | 64...127 => Tag::SameLocals1StackItemFrame(t), 84 | 247 => Tag::SameLocals1StackItemFrameExtended(t), 85 | 248...250 => Tag::ChopFrame(t), 86 | 251 => Tag::SameFrameExtended(t), 87 | 252...254 => Tag::AppendFrame(t), 88 | 255 => Tag::FullFrame(t), 89 | 128...246 => Tag::Reserved(t), 90 | _ => Tag::Unknown(t), 91 | } 92 | } 93 | } 94 | 95 | pub mod verification_type_info { 96 | use super::super::super::u1; 97 | use super::super::super::u2; 98 | use super::super::super::constant_pool_index; 99 | 100 | #[derive(Debug)] 101 | pub enum VerificationTypeInfo { 102 | Top, 103 | Integer, 104 | Float, 105 | Long, 106 | Double, 107 | Null, 108 | UninitializedThis, 109 | Object { class_index: constant_pool_index }, 110 | Uninitialized { 111 | /// The offset in the `code` array of the `Code` attribute that contains 112 | /// this `StackMapTable` attribute, of the _new_ instruction that 113 | /// created the object stored in the location. 114 | offset: u2, 115 | }, 116 | } 117 | 118 | #[derive(Debug, PartialEq)] 119 | pub enum Tag { 120 | Top, 121 | Integer, 122 | Float, 123 | Long, 124 | Double, 125 | Null, 126 | UninitializedThis, 127 | Object, 128 | Uninitialized, 129 | Unknown(u1), 130 | } 131 | 132 | impl From for Tag { 133 | fn from(t: u1) -> Self { 134 | match t { 135 | 0 => Tag::Top, 136 | 1 => Tag::Integer, 137 | 2 => Tag::Float, 138 | 4 => Tag::Long, 139 | 3 => Tag::Double, 140 | 5 => Tag::Null, 141 | 6 => Tag::UninitializedThis, 142 | 7 => Tag::Object, 143 | 8 => Tag::Uninitialized, 144 | _ => Tag::Unknown(t), 145 | } 146 | } 147 | } 148 | } 149 | } 150 | 151 | #[derive(Debug)] 152 | pub struct BootstrapMethod { 153 | /// An index into the `constant_pool` to a `ConstantPoolInfo::MethodHandle` structure. 154 | bootstrap_method_ref: constant_pool_index, 155 | /// The indices into the `constant_pool` to `ConstantPoolInfo::String`, 156 | /// `ConstantPoolInfo::Class`, `ConstantPoolInfo::Integer`, 157 | /// `ConstantPoolInfo::Long`, `ConstantPoolInfo::Float`, 158 | /// `ConstantPoolInfo::Double`, `ConstantPoolInfo::MethodHandle`, or 159 | /// `ConstantPoolInfo::MethodType`. 160 | bootstrap_arguments: Vec, 161 | } 162 | 163 | #[derive(Debug)] 164 | pub struct InnerClass { 165 | pub inner_class_info_index: constant_pool_index, 166 | pub outer_class_info_index: constant_pool_index, 167 | pub inner_name_index: constant_pool_index, 168 | pub inner_class_access_flags: inner_class_access_flags::t, 169 | } 170 | 171 | #[derive(Debug)] 172 | pub struct MethodParameter { 173 | pub name_index: constant_pool_index, 174 | pub access_flags: parameter_access_flags::t, 175 | } 176 | 177 | #[derive(Debug)] 178 | pub struct LineNumberInfo { 179 | pub start_pc: u2, 180 | pub line_number: u2, 181 | } 182 | 183 | #[derive(Debug)] 184 | pub struct LocalVariableInfo { 185 | pub start_pc: u2, 186 | pub length: u2, 187 | pub name_index: u2, 188 | pub descriptor_index: u2, 189 | pub index: u2, 190 | } 191 | 192 | #[derive(Debug)] 193 | pub struct LocalVariableTypeInfo { 194 | pub start_pc: u2, 195 | pub length: u2, 196 | pub name_index: u2, 197 | pub signature_index: u2, 198 | pub index: u2, 199 | } 200 | 201 | /// Attributes are used in the `ClassFile`, `FieldInfo`, `MethodInfo`, and 202 | /// `AttributeInfo::Code` structures of the class file format (§4.1, §4.5, §4.6, 203 | /// §4.7.3). 204 | #[derive(Debug)] 205 | pub enum AttributeInfo { 206 | ConstantValue { constant_value_index: constant_pool_index }, 207 | Code { 208 | max_stack: u2, 209 | max_locals: u2, 210 | code: Vec, 211 | exception_table: Vec, 212 | attributes: Vec, 213 | }, 214 | StackMapTable { 215 | entries: Vec, 216 | }, 217 | Exceptions { 218 | /// Contains indices into the `constant_pool` table for the class type 219 | /// that the method is declared to throw. 220 | exception_index_table: Vec, 221 | }, 222 | BootstrapMethods { 223 | bootstrap_methods: Vec 224 | }, 225 | 226 | InnerClasses { 227 | classes: Vec 228 | }, 229 | EnclosingMethod { 230 | class_index: constant_pool_index, 231 | method_index: constant_pool_index, 232 | }, 233 | Synthetic, 234 | Signature { 235 | /// A valid index into the `constant_pool` table for a `ConstantPoolInfo::Utf8` structure. 236 | signature_index: constant_pool_index, 237 | }, 238 | RuntimeVisibleAnnotations { 239 | annotations: Vec, 240 | }, 241 | RuntimeInvisibleAnnotations { 242 | annotations: Vec, 243 | }, 244 | RuntimeVisibleParameterAnnotations { 245 | parameter_annotations: Vec>, 246 | }, 247 | RuntimeInvisibleParameterAnnotations { 248 | parameter_annotations: Vec>, 249 | }, 250 | RuntimeVisibleTypeAnnotations { 251 | annotations: Vec, 252 | }, 253 | RuntimeInvisibleTypeAnnotations { 254 | annotations: Vec, 255 | }, 256 | AnnotationDefault { 257 | default_value: annotation::ElementValue, 258 | }, 259 | MethodParameters { 260 | parameters: Vec, 261 | }, 262 | 263 | SourceFile { 264 | sourcefile_index: constant_pool_index, 265 | }, 266 | SourceDebugExtension { 267 | debug_extension: Vec, 268 | }, 269 | LineNumberTable { 270 | line_number_table: Vec, 271 | }, 272 | LocalVariableTable { 273 | local_variable_table: Vec, 274 | }, 275 | LocalVariableTypeTable { 276 | local_variable_type_table: Vec, 277 | }, 278 | Deprecated, 279 | Unknown { 280 | /// A valid index into the `constant_pool` table. The `constant_pool` 281 | /// entry at that index must be a valid `ConstantPoolInfo::Utf8` 282 | /// structure representing the name of the attribute. 283 | attribute_name_index: constant_pool_index, 284 | /// The data for this attribute. 285 | info: Vec, 286 | }, 287 | } 288 | -------------------------------------------------------------------------------- /src/model/class_file/constant_pool.rs: -------------------------------------------------------------------------------- 1 | //! Contains structures to describe the constant pool 2 | //! [§4.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4). 3 | 4 | use util::one_indexed_vec::OneIndexedVec; 5 | 6 | use super::u1; 7 | use super::u2; 8 | use super::u4; 9 | 10 | #[allow(non_camel_case_types)] 11 | pub type constant_pool_index = u2; 12 | 13 | /// Values of constant pool tags [Table 14 | /// 4.4-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4-140). 15 | pub mod tags { 16 | use super::super::u1; 17 | pub const CLASS: u1 = 7; 18 | pub const FIELD_REF: u1 = 9; 19 | pub const METHOD_REF: u1 = 10; 20 | pub const INTERFACE_METHOD_REF: u1 = 11; 21 | pub const STRING: u1 = 8; 22 | pub const INTEGER: u1 = 3; 23 | pub const FLOAT: u1 = 4; 24 | pub const LONG: u1 = 5; 25 | pub const DOUBLE: u1 = 6; 26 | pub const NAME_AND_TYPE: u1 = 12; 27 | pub const UTF_8: u1 = 1; 28 | pub const METHOD_HANDLE: u1 = 15; 29 | pub const METHOD_TYPE: u1 = 16; 30 | pub const INVOKE_DYNAMIC: u1 = 18; 31 | } 32 | 33 | /// Type-safe representation of constant pool tags [Table 34 | /// 4.4-A](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4-140). 35 | #[derive(Debug, PartialEq)] 36 | pub enum Tag { 37 | Class, 38 | FieldRef, 39 | MethodRef, 40 | InterfaceMethodRef, 41 | String, 42 | Integer, 43 | Float, 44 | Long, 45 | Double, 46 | NameAndType, 47 | Utf8, 48 | MethodHandle, 49 | MethodType, 50 | InvokeDynamic, 51 | Unknown(u1), 52 | } 53 | 54 | impl From for Tag { 55 | fn from(tag: u1) -> Self { 56 | match tag { 57 | tags::CLASS => Tag::Class, 58 | tags::FIELD_REF => Tag::FieldRef, 59 | tags::METHOD_REF => Tag::MethodRef, 60 | tags::INTERFACE_METHOD_REF => Tag::InterfaceMethodRef, 61 | tags::STRING => Tag::String, 62 | tags::INTEGER => Tag::Integer, 63 | tags::FLOAT => Tag::Float, 64 | tags::LONG => Tag::Long, 65 | tags::DOUBLE => Tag::Double, 66 | tags::NAME_AND_TYPE => Tag::NameAndType, 67 | tags::UTF_8 => Tag::Utf8, 68 | tags::METHOD_HANDLE => Tag::MethodHandle, 69 | tags::METHOD_TYPE => Tag::MethodType, 70 | tags::INVOKE_DYNAMIC => Tag::InvokeDynamic, 71 | _ => Tag::Unknown(tag), 72 | } 73 | } 74 | } 75 | 76 | pub mod reference_kind { 77 | use super::super::u1; 78 | 79 | pub mod tags { 80 | use super::super::super::u1; 81 | pub const GET_FIELD: u1 = 1; 82 | pub const GET_STATIC: u1 = 2; 83 | pub const PUT_FIELD: u1 = 3; 84 | pub const PUT_STATIC: u1 = 4; 85 | pub const INVOKE_VIRTUAL: u1 = 5; 86 | pub const INVOKE_STATIC: u1 = 6; 87 | pub const INVOKE_SPECIAL: u1 = 7; 88 | pub const NEW_INVOKE_SPECIAL: u1 = 8; 89 | pub const INVOKE_INTERFACE: u1 = 9; 90 | } 91 | 92 | #[derive(Debug, PartialEq)] 93 | pub enum Tag { 94 | GetField, 95 | GetStatic, 96 | PutField, 97 | PutStatic, 98 | InvokeVirtual, 99 | InvokeStatic, 100 | InvokeSpecial, 101 | NewInvokeSpecial, 102 | InvokeInterface, 103 | Unknown(u1), 104 | } 105 | 106 | impl From for Tag { 107 | fn from(tag: u1) -> Self { 108 | match tag { 109 | tags::GET_FIELD => Tag::GetField, 110 | tags::GET_STATIC => Tag::GetStatic, 111 | tags::PUT_FIELD => Tag::PutField, 112 | tags::PUT_STATIC => Tag::PutStatic, 113 | tags::INVOKE_VIRTUAL => Tag::InvokeVirtual, 114 | tags::INVOKE_STATIC => Tag::InvokeStatic, 115 | tags::INVOKE_SPECIAL => Tag::InvokeSpecial, 116 | tags::NEW_INVOKE_SPECIAL => Tag::NewInvokeSpecial, 117 | tags::INVOKE_INTERFACE => Tag::InvokeInterface, 118 | _ => Tag::Unknown(tag), 119 | } 120 | } 121 | } 122 | } 123 | 124 | #[derive(Debug, PartialEq)] 125 | pub enum MethodReference { 126 | GetField { reference_index: constant_pool_index }, 127 | GetStatic { reference_index: constant_pool_index }, 128 | PutField { reference_index: constant_pool_index }, 129 | PutStatic { reference_index: constant_pool_index }, 130 | InvokeVirtual { reference_index: constant_pool_index }, 131 | InvokeStatic { reference_index: constant_pool_index }, 132 | InvokeSpecial { reference_index: constant_pool_index }, 133 | NewInvokeSpecial { reference_index: constant_pool_index }, 134 | InvokeInterface { reference_index: constant_pool_index }, 135 | } 136 | 137 | #[derive(Debug, PartialEq)] 138 | pub enum ConstantPoolInfo { 139 | /// The `CONSTANT_Class_info` structure 140 | /// [§4.4.1](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.1). 141 | Class { name_index: constant_pool_index }, 142 | FieldRef { class_index: constant_pool_index, name_and_type_index: constant_pool_index }, 143 | MethodRef { class_index: constant_pool_index, name_and_type_index: constant_pool_index }, 144 | InterfaceMethodRef { 145 | class_index: constant_pool_index, 146 | name_and_type_index: constant_pool_index 147 | }, 148 | String { string_index: u2 }, 149 | Integer { bytes: u4 }, 150 | Float { bytes: u4 }, 151 | Long { high_bytes: u4, low_bytes: u4 }, 152 | Double { high_bytes: u4, low_bytes: u4 }, 153 | NameAndType { 154 | name_index: constant_pool_index, 155 | descriptor_index: constant_pool_index, 156 | }, 157 | Utf8 { bytes: Vec }, 158 | MethodHandle { reference: MethodReference }, 159 | MethodType { descriptor_index: constant_pool_index }, 160 | InvokeDynamic { 161 | /// A valid index into the `bootstrap_methods` array of the bootstrap method table. 162 | bootstrap_method_attr_index: constant_pool_index, 163 | /// A valid index into the `constant_pool` table. The `constant_pool` entry at that index 164 | /// must be a valid `ConstantPoolInfo::Utf8` structure representing the name of the 165 | /// attribute. 166 | name_and_type_index: constant_pool_index, 167 | }, 168 | /// Indicates an unusable constant pool entry. 169 | /// 170 | /// All 8-byte constants take up two entries in the constant_pool table of the class file. If a 171 | /// `CONSTANT_Long_info` or `CONSTANT_Double_info` structure is the item in the `constant_pool` 172 | /// table at index _n_, then the next usable item in the pool is located at index _n_ + 2. The 173 | /// constant_pool index _n_ + 1 must be valid but is considered unusable. 174 | Unusable, 175 | } 176 | 177 | impl ConstantPoolInfo { 178 | pub fn tag(&self) -> Tag { 179 | match *self { 180 | ConstantPoolInfo::Class { .. } => Tag::Class, 181 | ConstantPoolInfo::FieldRef { .. } => Tag::FieldRef, 182 | ConstantPoolInfo::MethodRef { .. } => Tag::MethodRef, 183 | ConstantPoolInfo::InterfaceMethodRef { .. } => Tag::InterfaceMethodRef, 184 | ConstantPoolInfo::String { .. } => Tag::String, 185 | ConstantPoolInfo::Integer { .. } => Tag::Integer, 186 | ConstantPoolInfo::Float { .. } => Tag::Float, 187 | ConstantPoolInfo::Long { .. } => Tag::Long, 188 | ConstantPoolInfo::Double { .. } => Tag::Double, 189 | ConstantPoolInfo::NameAndType { .. } => Tag::NameAndType, 190 | ConstantPoolInfo::Utf8 { .. } => Tag::Utf8, 191 | ConstantPoolInfo::MethodHandle { .. } => Tag::MethodHandle, 192 | ConstantPoolInfo::MethodType { .. } => Tag::MethodType, 193 | ConstantPoolInfo::InvokeDynamic { .. } => Tag::InvokeDynamic, 194 | ConstantPoolInfo::Unusable => 195 | panic!("unusable constant pool entry does not have a valid tag"), 196 | } 197 | } 198 | } 199 | 200 | /// The constant pool 201 | /// [§4.4](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4). 202 | pub type ConstantPool = OneIndexedVec; 203 | 204 | impl ConstantPool { 205 | pub fn from_zero_indexed_vec(vec: Vec) -> Self { 206 | OneIndexedVec::from(vec) 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/model/class_file/mod.rs: -------------------------------------------------------------------------------- 1 | //! The `ClassFile` structure of 2 | //! [§4.1](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1). 3 | 4 | pub mod access_flags; 5 | pub mod attribute; 6 | pub mod constant_pool; 7 | 8 | pub use self::access_flags::class_access_flags; 9 | pub use self::access_flags::field_access_flags; 10 | pub use self::access_flags::method_access_flags; 11 | pub use self::attribute::AttributeInfo; 12 | pub use self::constant_pool::ConstantPoolInfo; 13 | pub use self::constant_pool::ConstantPool; 14 | 15 | /// Represents an unsigned one-byte quantity. 16 | #[allow(non_camel_case_types)] 17 | pub type u1 = u8; 18 | 19 | /// Represents an unsigned two-byte quantity. 20 | #[allow(non_camel_case_types)] 21 | pub type u2 = u16; 22 | 23 | /// Represents an unsigned four-byte quantity. 24 | #[allow(non_camel_case_types)] 25 | pub type u4 = u32; 26 | 27 | /// Represents an index into the constant pool. 28 | #[allow(non_camel_case_types)] 29 | pub type constant_pool_index = constant_pool::constant_pool_index; 30 | 31 | #[derive(Debug)] 32 | pub struct FieldInfo { 33 | /// Mask of flags used to denote access permissions to and properties of 34 | /// this field. 35 | pub access_flags: field_access_flags::t, 36 | /// A valid index into the `constant_pool` table. The `constant_pool` entry 37 | /// at that index must be a `ConstantPoolInfo::Utf8` structure representing 38 | /// a valid unqualified name denoting a field. 39 | pub name_index: constant_pool_index, 40 | /// A valid index into the `constant_pool` table. The `constant_pool` entry 41 | /// at that index must be a `ConstantPoolInfo::Utf8` structure representing 42 | /// a valid unqualified name denoting a field. 43 | pub descriptor_index: constant_pool_index, 44 | /// The attributes associated with this field. 45 | pub attributes: Vec, 46 | } 47 | 48 | #[derive(Debug)] 49 | pub struct MethodInfo { 50 | /// Mask of flags used to denote access permissions to and properties of 51 | /// this class or interface. See the documentation for `ClassAccessFlags` 52 | /// for the interpretation of each flag. 53 | pub access_flags: method_access_flags::t, 54 | /// A valid index into the `constant_pool` table. The `constant_pool` entry 55 | /// at that index must be a `ConstantPoolInfo::Utf8` structure representing 56 | /// a valid unqualified name denoting a method. 57 | pub name_index: u2, 58 | /// A valid index into the `constant_pool` table. The `constant_pool` entry 59 | /// at that index must be a `ConstantPoolInfo::Utf8` structure representing 60 | /// a valid method descriptor. 61 | pub descriptor_index: u2, 62 | /// The attributes associated with this method. 63 | pub attributes: Vec, 64 | } 65 | 66 | #[derive(Debug)] 67 | pub struct ClassFile { 68 | /// Minor version number 69 | pub minor_version: u2, 70 | /// Major version number 71 | pub major_version: u2, 72 | /// Table of structures representing various string constants, class and 73 | /// interface names, field names, and other constants. The `constant_pool` 74 | /// table is indexed from 1 to `constant_pool_count - 1`. 75 | pub constant_pool: ConstantPool, 76 | /// Mask of flags used to denote access permissions to and properties of 77 | /// this class or interface. See the documentation for `ClassAccessFlags` 78 | /// for the interpretation of each flag. 79 | pub access_flags: class_access_flags::t, 80 | /// A valid index into the `constant_pool` table. The `constant_pool` entry 81 | /// at that index must be a `ConstantPoolInfo::Class` structure representing 82 | /// a valid unqualified name denoting a field. 83 | pub this_class: constant_pool_index, 84 | /// For a class, must be either zero or a valid index into the 85 | /// `constant_pool` table. If the value of `super_class` is non-zero, then 86 | /// the `constant_pool` entry at that index must be a `ConstantPoolInfo::Class` 87 | /// structure denoting the direct superclass of the class defined by this 88 | /// class file. Neither the direct superclass nor any of its superclasses 89 | /// may have the `ACC_FINAL` flag set in the `access_flags` item of its 90 | /// `ClassFile` structure. 91 | pub super_class: constant_pool_index, 92 | /// Each value in `interfaces` mut be a valid index into the `constant_pool` 93 | /// table. The `constant_pool` entry at each value of `interfaces[i]`, where 94 | /// `0 ≤ i < interfaces_count`, must be a `ConstantPoolInfo::Class` structure 95 | /// representing an interface that is a direct superinterface of this class 96 | /// or interface type, in the left-to-right order given in the source for 97 | /// the type. 98 | pub interfaces: Vec, 99 | /// Contains only those fields declared by this class or interface. Does not 100 | /// include items representing fields that are inherited from superclasses 101 | /// or superinterfaces. 102 | pub fields: Vec, 103 | /// Contains only those methods declared by this class or interface. Does 104 | /// not include items representing methods that are inherited from 105 | /// superclasses or superinterfaces. 106 | pub methods: Vec, 107 | /// Contains the attributes of this class. 108 | pub attributes: Vec, 109 | } 110 | -------------------------------------------------------------------------------- /src/model/mod.rs: -------------------------------------------------------------------------------- 1 | //! Structures for the [Java SE 8 JVM class file 2 | //! format](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html). 3 | 4 | pub mod class_file; 5 | -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | //! Contains a parser for a Java class file. 2 | //! 3 | //! # Examples 4 | //! 5 | //! Basic usage: 6 | //! ``` 7 | //! let data = include_bytes!("../../data/HelloWorld.class"); 8 | //! assert!(parse_class_file(data).is_done()); // returns a nom::IResult 9 | //! ``` 10 | 11 | #[macro_use] 12 | pub mod nom_support; 13 | 14 | pub mod class_file; 15 | -------------------------------------------------------------------------------- /src/parser/nom_support.rs: -------------------------------------------------------------------------------- 1 | //! Workaround to get sane nested errors from `nom`. The nom library does not provide parsers that 2 | //! can have controllable backtracking behavior, so we need to hack it in. Follows 3 | //! [#160](https://github.com/Geal/nom/issues/160). 4 | //! 5 | //! This module contains support for converting to and from nom parsers and 6 | //! backtracking-controllable parsers. A _parser that can be controlled for backtracking_ has type 7 | //! `std::result::Result>`, while a _nom parser_ has type 8 | //! `nom::IResult`. 9 | 10 | /// `done!(O) => nom::IResult::Done` wraps the specified expression in `nom::IResult::Done`. 11 | #[macro_export] 12 | macro_rules! done { 13 | ($i: expr, $e: expr) => ($crate::nom::IResult::Done($i, $e)); 14 | } 15 | 16 | /// Convenience for creating a custom nom error `nom::Err::Code(nom::ErrorKind::Custom($err))`. 17 | #[macro_export] 18 | macro_rules! custom_error { 19 | ($e: expr) => ($crate::nom::Err::Code($crate::nom::ErrorKind::Custom($e))); 20 | } 21 | 22 | /// Adds a custom error if the child nom parser fails. 23 | #[macro_export] 24 | macro_rules! p_add_error { 25 | ($i: expr, $e: expr, $($args: tt)*) => (add_error!($i, ErrorKind::Custom($e), $($args)*)) 26 | } 27 | 28 | /// Declares a parser (with a body of type `nom::IResult`) that can be controlled for backtracking 29 | /// by using `c!` and `cut!`. The return type is `std::result::Result>`. If the parser returns `std::result::Result::Ok`, then backtracking occurs. If the parser 31 | /// returns `std::result::Result::Err`, then backtracking does not occur. 32 | #[macro_export] 33 | macro_rules! n { 34 | ($name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => ( 35 | fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i,$o,u32>, $crate::nom::Err<$i, u32>> { 36 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 37 | } 38 | ); 39 | ($name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => ( 40 | fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, $e>, $crate::nom::Err<$i, $e>> { 41 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 42 | } 43 | ); 44 | ($name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => ( 45 | fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, u32>, $crate::nom::Err<$i, u32>> { 46 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 47 | } 48 | ); 49 | ($name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => ( 50 | fn $name<'a>( i: &'a[u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&'a [u8], $o, u32>, $crate::nom::Err<&'a [u8], u32>> { 51 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 52 | } 53 | ); 54 | ($name:ident, $submac:ident!( $($args:tt)* )) => ( 55 | fn $name( i: &[u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&[u8], &[u8], u32>, $crate::nom::Err<&[u8], u32>> { 56 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 57 | } 58 | ); 59 | (pub $name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => ( 60 | pub fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i,$o, u32>, $crate::nom::Err<$i, u32>> { 61 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 62 | } 63 | ); 64 | (pub $name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => ( 65 | pub fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, $e>, $crate::nom::Err<$i, $e>> { 66 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 67 | } 68 | ); 69 | (pub $name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => ( 70 | pub fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, u32>, $crate::nom::Err<$i, u32>> { 71 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 72 | } 73 | ); 74 | (pub $name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => ( 75 | pub fn $name( i: &[u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&[u8], $o, u32>, $crate::nom::Err<&[u8], u32>> { 76 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 77 | } 78 | ); 79 | (pub $name:ident, $submac:ident!( $($args:tt)* )) => ( 80 | pub fn $name<'a>( i: &'a [u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&[u8], &[u8], u32>, $crate::nom::Err<&[u8], u32>> { 81 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 82 | } 83 | ); 84 | ($name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => ( 85 | fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, $e>, $crate::nom::Err<$i, $e>> { 86 | $crate::std::result::Result::Ok($submac!(i, $($args)*)) 87 | } 88 | ); 89 | } 90 | 91 | /// Declares a parser (with a body of type `std::result::Result>`) 92 | /// that can be controlled for backtracking by using `c!` and `cut!`. The return type is 93 | /// `std::result::Result>`. If the parser returns 94 | /// `std::result::Result::Ok`, then backtracking occurs. If the parser returns 95 | /// `std::result::Result::Err`, then backtracking does not occur. 96 | #[macro_export] 97 | macro_rules! p_named ( 98 | ($name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => ( 99 | fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i,$o,u32>, $crate::nom::Err<$i, u32>> { 100 | $submac!(i, $($args)*) 101 | } 102 | ); 103 | ($name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => ( 104 | fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, $e>, $crate::nom::Err<$i, $e>> { 105 | $submac!(i, $($args)*) 106 | } 107 | ); 108 | ($name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => ( 109 | fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, u32>, $crate::nom::Err<$i, u32>> { 110 | $submac!(i, $($args)*) 111 | } 112 | ); 113 | ($name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => ( 114 | fn $name<'a>( i: &'a[u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&'a [u8], $o, u32>, $crate::nom::Err<&'a [u8], u32>> { 115 | $submac!(i, $($args)*) 116 | } 117 | ); 118 | ($name:ident, $submac:ident!( $($args:tt)* )) => ( 119 | fn $name( i: &[u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&[u8], &[u8], u32>, $crate::nom::Err<&[u8], u32>> { 120 | $submac!(i, $($args)*) 121 | } 122 | ); 123 | (pub $name:ident( $i:ty ) -> $o:ty, $submac:ident!( $($args:tt)* )) => ( 124 | pub fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i,$o, u32>, $crate::nom::Err<$i, u32>> { 125 | $submac!(i, $($args)*) 126 | } 127 | ); 128 | (pub $name:ident<$i:ty,$o:ty,$e:ty>, $submac:ident!( $($args:tt)* )) => ( 129 | pub fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, $e>, $crate::nom::Err<$i, $e>> { 130 | $submac!(i, $($args)*) 131 | } 132 | ); 133 | (pub $name:ident<$i:ty,$o:ty>, $submac:ident!( $($args:tt)* )) => ( 134 | pub fn $name( i: $i ) -> $crate::std::result::Result<$crate::nom::IResult<$i, $o, u32>, $crate::nom::Err<$i, u32>> { 135 | $submac!(i, $($args)*) 136 | } 137 | ); 138 | (pub $name:ident<$o:ty>, $submac:ident!( $($args:tt)* )) => ( 139 | pub fn $name( i: &[u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&[u8], $o, u32>, $crate::nom::Err<&[u8], u32>> { 140 | $submac!(i, $($args)*) 141 | } 142 | ); 143 | (pub $name:ident, $submac:ident!( $($args:tt)* )) => ( 144 | pub fn $name<'a>( i: &'a [u8] ) -> $crate::std::result::Result<$crate::nom::IResult<&[u8], &[u8], u32>, $crate::nom::Err<&[u8], u32>> { 145 | $submac!(i, $($args)*) 146 | } 147 | ); 148 | 149 | ); 150 | 151 | /// `cut!(nom::ErrorKind, I -> nom::IResult) => Err<_, nom::Err> OR nom::IResult::Done OR IResult::Incomplete<_>` 152 | /// Prevents backtracking out of the specified nom parser. 153 | #[macro_export] 154 | macro_rules! cut { 155 | ($i: expr, $code: expr, $submac:ident !( $($args:tt)* )) => ({ 156 | let cl = || { 157 | Ok($submac!($i, $($args)*)) 158 | }; 159 | 160 | match cl() { 161 | $crate::std::result::Result::Ok($crate::nom::IResult::Incomplete(x)) => 162 | $crate::nom::IResult::Incomplete(x), 163 | $crate::std::result::Result::Ok($crate::nom::IResult::Done(i, o)) => 164 | $crate::nom::IResult::Done(i, o), 165 | $crate::std::result::Result::Ok( 166 | $crate::nom::IResult::Error(e)) | $crate::std::result::Result::Err(e) => 167 | return $crate::std::result::Result::Err($crate::nom::Err::NodePosition($code, $i, Box::new(e))), 168 | } 169 | }); 170 | ($i:expr, $code:expr, $f:expr) => (cut!($i, $code, call!($f))); 171 | } 172 | 173 | /// Converts a backtracking-controllable parser to a nom parser that will have the correct 174 | /// backtracking behavior. 175 | #[macro_export] 176 | macro_rules! c { 177 | ($i:expr, $f:expr) => ({ 178 | match $f($i) { 179 | $crate::std::result::Result::Ok($crate::nom::IResult::Incomplete(x)) => 180 | $crate::nom::IResult::Incomplete(x), 181 | $crate::std::result::Result::Ok($crate::nom::IResult::Done(i, o)) => 182 | $crate::nom::IResult::Done(i, o), 183 | $crate::std::result::Result::Ok($crate::nom::IResult::Error(e)) => 184 | $crate::nom::IResult::Error(e), 185 | $crate::std::result::Result::Err(e) => return $crate::std::result::Result::Err(e), 186 | } 187 | }); 188 | ($i:expr, $f:expr, $($arg: expr),* ) => ({ 189 | match $f($i, $($arg),*) { 190 | $crate::std::result::Result::Ok($crate::nom::IResult::Incomplete(x)) => 191 | $crate::nom::IResult::Incomplete(x), 192 | $crate::std::result::Result::Ok($crate::nom::IResult::Done(i, o)) => 193 | $crate::nom::IResult::Done(i, o), 194 | $crate::std::result::Result::Ok($crate::nom::IResult::Error(e)) => 195 | $crate::nom::IResult::Error(e), 196 | $crate::std::result::Result::Err(e) => return $crate::std::result::Result::Err(e), 197 | } 198 | }); 199 | } 200 | 201 | /// `p_cut!(E, I -> nom::IResult) => Err<_, nom::Err> OR IResult::Done OR IResult::Incomplete<_>` 202 | /// Like `cut!`, but with a custom error type. 203 | #[macro_export] 204 | macro_rules! p_cut { 205 | ($i: expr, $err: expr, $($args: tt)*) => (cut!($i, ErrorKind::Custom($err), $($args)*)); 206 | ($i: expr, $err: expr, $f: expr) => (cut!($i, $err, call!($f))); 207 | } 208 | 209 | /// Returns a custom error for a nom parser. 210 | #[macro_export] 211 | macro_rules! p_nom_error { 212 | ($e: expr) => ($crate::nom::IResult::Error(custom_error!($e))); 213 | } 214 | 215 | /// Returns a custom error for a backtracking-controllable parser. 216 | #[macro_export] 217 | macro_rules! p_fail { 218 | ($e: expr) => (return $crate::std::result::Result::Err(custom_error!($e))); 219 | } 220 | 221 | /// Binds monadically without backtracking the result of a backtracking-controllable parser. 222 | #[macro_export] 223 | macro_rules! p_unwrap { 224 | ($r: expr) => ({ 225 | match $r { 226 | $crate::std::result::Result::Ok($crate::nom::IResult::Done(i, o)) => (i, o), 227 | $crate::std::result::Result::Ok($crate::nom::IResult::Incomplete(n)) => 228 | return $crate::std::result::Result::Ok($crate::nom::IResult::Incomplete(n)), 229 | $crate::std::result::Result::Ok($crate::nom::IResult::Error(e)) => 230 | return $crate::std::result::Result::Err(e), 231 | $crate::std::result::Result::Err(e) => return $crate::std::result::Result::Err(e), 232 | } 233 | }) 234 | } 235 | 236 | /// Wraps the result of a nom parser (`nom::IResult`) to be non-backtracking. 237 | #[macro_export] 238 | macro_rules! wrap_nom { 239 | ($r: expr) => ({ 240 | match $r { 241 | $crate::nom::IResult::Done(i, o) => 242 | $crate::std::result::Result::Ok($crate::nom::IResult::Done(i, o)), 243 | i @ $crate::nom::IResult::Incomplete(_) => $crate::std::result::Result::Ok(i), 244 | $crate::nom::IResult::Error(e) => return $crate::std::result::Result::Err(e), 245 | } 246 | }) 247 | } 248 | 249 | /// Wraps a nom parser (returning `nom::IResult`) to produce a parser that does not 250 | /// backtrack on error. 251 | #[macro_export] 252 | macro_rules! p_wrap_nom { 253 | ($i: expr, $submac: ident ! ( $($args: tt)* )) => (wrap_nom!($submac!($i, $($args)*))); 254 | ($i: expr, $f: expr) => (p_wrap_nom!($i, call!($f))); 255 | } 256 | 257 | /// Binds monadically without backtracking a backtracking-controllable parser. 258 | #[macro_export] 259 | macro_rules! p_try { 260 | ($i: expr, $submac: ident ! ( $($args:tt)* )) => (match $submac!($i, $($args)*) { 261 | $crate::std::result::Result::Ok($crate::nom::IResult::Done(i, o)) => (i, o), 262 | $crate::std::result::Result::Ok($crate::nom::IResult::Error(e)) => 263 | return $crate::std::result::Result::Ok($crate::nom::IResult::Error(e)), 264 | $crate::std::result::Result::Ok($crate::nom::IResult::Incomplete(i)) => 265 | return $crate::std::result::Result::Ok($crate::nom::IResult::Incomplete(i)), 266 | $crate::std::result::Result::Err(e) => return $crate::std::result::Result::Err(e), 267 | }); 268 | ($i: expr, $f: expr) => ( 269 | p_try!($i, call!($f)) 270 | ); 271 | } 272 | 273 | #[cfg(test)] 274 | mod test { 275 | use nom::IResult; 276 | use nom::Err; 277 | use nom::ErrorKind; 278 | 279 | n!(pub foo< bool >, 280 | chain!( 281 | tag!("a") ~ 282 | cut!($crate::nom::ErrorKind::Custom(42), tag!("b")) , 283 | || { true } 284 | ) 285 | ); 286 | 287 | n!(pub foos< Vec >, 288 | delimited!( 289 | tag!("("), 290 | many0!(c!(foo)), 291 | tag!(")") 292 | ) 293 | ); 294 | 295 | #[test] 296 | fn test_ok() { 297 | let r = foos(b"(abab)"); 298 | println!("result: {:?}", r); 299 | match r { 300 | Ok(IResult::Done(_,result)) => assert_eq!(result, vec![true,true]), 301 | res => panic!("Oops {:?}.",res) 302 | } 303 | } 304 | 305 | #[test] 306 | fn test_err() { 307 | let r = foos(b"(ac)"); 308 | println!("result: {:?}", r); 309 | match r { 310 | Err(Err::NodePosition(kind, _, _)) => assert_eq!(kind, ErrorKind::Custom(42)), 311 | res => panic!("Oops, {:?}",res) 312 | } 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod modified_utf8; 2 | pub mod one_indexed_vec; 3 | -------------------------------------------------------------------------------- /src/util/modified_utf8.rs: -------------------------------------------------------------------------------- 1 | //! Modified UTF-8 string slices. 2 | //! 3 | //! See [§4.4.7](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.7). 4 | 5 | use std::char; 6 | 7 | /// Errors which can occur when attempting to interpret a sequence of `u8` as a modified UTF-8 8 | /// string. 9 | #[derive(Debug)] 10 | pub struct ModifiedUtf8Error { 11 | valid_up_to: usize, 12 | } 13 | 14 | /// Converts a slice of bytes in modified UTF-8 encoding to a string slice. 15 | pub fn from_modified_utf8(bytes: &[u8]) -> Result { 16 | // Refer to §4.4.7 for more information about the modified UTF-8 encoding. 17 | let mut result = String::new(); 18 | let mut offset = 0; 19 | let len = bytes.len(); 20 | while offset < len { 21 | let old_offset = offset; 22 | macro_rules! err { 23 | () => (return Err(ModifiedUtf8Error { valid_up_to: old_offset })); 24 | } 25 | macro_rules! next { 26 | () => ({ 27 | offset += 1; 28 | if offset >= len { 29 | err!(); 30 | } 31 | bytes[offset] 32 | }); 33 | } 34 | 35 | let x = bytes[offset]; 36 | if x < 0b0111_1111 { // pattern: 0xxxxxx 37 | result.push(x as char); 38 | } else if x < 0b1101_1111 { // pattern: 110xxxxx 39 | let y = next!(); 40 | if y < 0b1011_111 { // pattern: 10xxxxxx 41 | let c = ((x & 0x1f) << 6) + (y & 0x3f); 42 | result.push(c as char); 43 | } else { 44 | err!() 45 | } 46 | } else if x < 0b1110_1111 { // pattern: 1110xxxx 47 | let y = next!(); 48 | if y < 0b1011_1111 { // pattern: 10xxxxxx 49 | let z = next!(); 50 | if z < 0b1011_1111 { // pattern: 10xxxxxx 51 | let q: u32 = (((x & 0xf) as u32) << 12) + (((y & 0x3f) as u32) << 6) 52 | + ((z & 0x3f) as u32); 53 | let c = unsafe { char::from_u32_unchecked(q) }; 54 | result.push(c); 55 | } else { 56 | err!() 57 | } 58 | } else { 59 | err!() 60 | } 61 | } else if x == 0b1110_1101 { // pattern: 11101101 62 | let v = next!(); 63 | if v < 0b1010_1111 { // pattern: 10101111 64 | let w = next!(); 65 | if w < 0b1011_1111 { // pattern: 10xxxxxx 66 | let xx = next!(); 67 | if xx == 0b1110_1101 { // pattern: 11101101 68 | let y = next!(); 69 | if y < 0b1011_1111 { // pattern: 1011xxxx 70 | let z = next!(); 71 | if z < 0b1011_1111 { // pattern: 10xxxxxx 72 | let q: u32 = 0x10000u32 + (((v & 0x0f) as u32) << 16) 73 | + (((w & 0x3f) as u32) << 10) 74 | + (((y & 0x0f) as u32) << 6) + ((z & 0x3f) as u32); 75 | let c = unsafe { char::from_u32_unchecked(q) }; 76 | result.push(c); 77 | } else { 78 | err!() 79 | } 80 | } else { 81 | err!() 82 | } 83 | } else { 84 | err!() 85 | } 86 | } else { 87 | err!() 88 | } 89 | } else { 90 | err!() 91 | } 92 | } else { 93 | err!() 94 | } 95 | 96 | offset += 1; 97 | } 98 | Ok(result) 99 | } 100 | 101 | // TODO: Test implementation of from_modified_utf8 102 | #[cfg(test)] 103 | mod test { 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/util/one_indexed_vec.rs: -------------------------------------------------------------------------------- 1 | //! A `std::vec::Vec`, but 1-indexed instead of 0-indexed. 2 | 3 | use std::ops::Index; 4 | use std::ops::IndexMut; 5 | 6 | /// Like a `std::vec::Vec`, but 1-indexed instead of 0-indexed. 7 | #[derive(Debug)] 8 | pub struct OneIndexedVec { 9 | vec: Vec, 10 | } 11 | 12 | impl OneIndexedVec { 13 | /// Returns the element of a slice at the given index, or None if the index is out of bounds. 14 | pub fn get(&self, index: usize) -> Option<&T> { 15 | if index == 0 { 16 | panic!("index is 0"); 17 | } 18 | self.vec.get(index - 1) 19 | } 20 | 21 | /// Returns the number of elements in the slice. 22 | pub fn len(&self) -> usize { 23 | self.vec.len() 24 | } 25 | 26 | /// Returns true if the slice has a length of 0. 27 | pub fn is_empty(&self) -> bool { 28 | self.vec.is_empty() 29 | } 30 | 31 | /// Returns an iterator over the slice. 32 | pub fn iter(&self) -> ::std::slice::Iter { 33 | self.vec.iter() 34 | } 35 | 36 | /// Returns an iterator that allows modifying each value. 37 | pub fn iter_mut(&mut self) -> ::std::slice::IterMut { 38 | self.vec.iter_mut() 39 | } 40 | } 41 | 42 | impl Index for OneIndexedVec { 43 | type Output = T; 44 | fn index(&self, index: usize) -> &Self::Output { 45 | if index == 0 { 46 | panic!("index is 0"); 47 | } 48 | &self.vec[index - 1] 49 | } 50 | } 51 | 52 | impl IndexMut for OneIndexedVec { 53 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { 54 | if index == 0 { 55 | panic!("index is 0"); 56 | } 57 | &mut self.vec[index - 1] 58 | } 59 | } 60 | 61 | impl From> for OneIndexedVec { 62 | fn from(vec: Vec) -> Self { 63 | OneIndexedVec { vec: vec } 64 | } 65 | } 66 | 67 | impl IntoIterator for OneIndexedVec { 68 | type Item = T; 69 | type IntoIter = ::std::vec::IntoIter; 70 | fn into_iter(self) -> Self::IntoIter { 71 | self.vec.into_iter() 72 | } 73 | } 74 | 75 | impl<'a, T> IntoIterator for &'a OneIndexedVec { 76 | type Item = &'a T; 77 | type IntoIter = ::std::slice::Iter<'a, T>; 78 | fn into_iter(self) -> Self::IntoIter { 79 | self.vec.iter() 80 | } 81 | } 82 | 83 | impl<'a, T> IntoIterator for &'a mut OneIndexedVec { 84 | type Item = &'a mut T; 85 | type IntoIter = ::std::slice::IterMut<'a, T>; 86 | fn into_iter(self) -> Self::IntoIter { 87 | self.vec.iter_mut() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/vm/bytecode.rs: -------------------------------------------------------------------------------- 1 | //! The Java bytecode. 2 | 3 | #[allow(dead_code)] 4 | /// Opcodes for Java bytecode instructions. 5 | pub mod opcode { 6 | pub const NOP: u8 = 0x00; 7 | pub const ACONST_NULL: u8 = 0x01; 8 | pub const ICONST_M1: u8 = 0x02; 9 | pub const ICONST_0: u8 = 0x03; 10 | pub const ICONST_1: u8 = 0x04; 11 | pub const ICONST_2: u8 = 0x05; 12 | pub const ICONST_3: u8 = 0x06; 13 | pub const ICONST_4: u8 = 0x07; 14 | pub const ICONST_5: u8 = 0x08; 15 | pub const LCONST_0: u8 = 0x09; 16 | pub const LCONST_1: u8 = 0x0a; 17 | pub const FCONST_0: u8 = 0x0b; 18 | pub const FCONST_1: u8 = 0x0c; 19 | pub const FCONST_2: u8 = 0x0d; 20 | pub const DCONST_0: u8 = 0x0e; 21 | pub const DCONST_1: u8 = 0x0f; 22 | pub const BIPUSH: u8 = 0x10; 23 | pub const SIPUSH: u8 = 0x11; 24 | pub const LDC: u8 = 0x12; 25 | pub const LDC_W: u8 = 0x13; 26 | pub const LDC2_W: u8 = 0x14; 27 | pub const ILOAD: u8 = 0x15; 28 | pub const LLOAD: u8 = 0x16; 29 | pub const FLOAD: u8 = 0x17; 30 | pub const DLOAD: u8 = 0x18; 31 | pub const ALOAD: u8 = 0x19; 32 | pub const ILOAD_0: u8 = 0x1a; 33 | pub const ILOAD_1: u8 = 0x1b; 34 | pub const ILOAD_2: u8 = 0x1c; 35 | pub const ILOAD_3: u8 = 0x1d; 36 | pub const LLOAD_0: u8 = 0x1e; 37 | pub const LLOAD_1: u8 = 0x1f; 38 | pub const LLOAD_2: u8 = 0x20; 39 | pub const LLOAD_3: u8 = 0x21; 40 | pub const FLOAD_0: u8 = 0x22; 41 | pub const FLOAD_1: u8 = 0x23; 42 | pub const FLOAD_2: u8 = 0x24; 43 | pub const FLOAD_3: u8 = 0x25; 44 | pub const DLOAD_0: u8 = 0x26; 45 | pub const DLOAD_1: u8 = 0x27; 46 | pub const DLOAD_2: u8 = 0x28; 47 | pub const DLOAD_3: u8 = 0x29; 48 | pub const ALOAD_0: u8 = 0x2a; 49 | pub const ALOAD_1: u8 = 0x2b; 50 | pub const ALOAD_2: u8 = 0x2c; 51 | pub const ALOAD_3: u8 = 0x2d; 52 | pub const IALOAD: u8 = 0x2e; 53 | pub const LALOAD: u8 = 0x2f; 54 | pub const FALOAD: u8 = 0x30; 55 | pub const DALOAD: u8 = 0x31; 56 | pub const AALOAD: u8 = 0x32; 57 | pub const BALOAD: u8 = 0x33; 58 | pub const CALOAD: u8 = 0x34; 59 | pub const SALOAD: u8 = 0x35; 60 | pub const ISTORE: u8 = 0x36; 61 | pub const LSTORE: u8 = 0x37; 62 | pub const FSTORE: u8 = 0x38; 63 | pub const DSTORE: u8 = 0x39; 64 | pub const ASTORE: u8 = 0x3a; 65 | pub const ISTORE_0: u8 = 0x3b; 66 | pub const ISTORE_1: u8 = 0x3c; 67 | pub const ISTORE_2: u8 = 0x3d; 68 | pub const ISTORE_3: u8 = 0x3e; 69 | pub const LSTORE_0: u8 = 0x3f; 70 | pub const LSTORE_1: u8 = 0x40; 71 | pub const LSTORE_2: u8 = 0x41; 72 | pub const LSTORE_3: u8 = 0x42; 73 | pub const FSTORE_0: u8 = 0x43; 74 | pub const FSTORE_1: u8 = 0x44; 75 | pub const FSTORE_2: u8 = 0x45; 76 | pub const FSTORE_3: u8 = 0x46; 77 | pub const DSTORE_0: u8 = 0x47; 78 | pub const DSTORE_1: u8 = 0x48; 79 | pub const DSTORE_2: u8 = 0x49; 80 | pub const DSTORE_3: u8 = 0x4a; 81 | pub const ASTORE_0: u8 = 0x4b; 82 | pub const ASTORE_1: u8 = 0x4c; 83 | pub const ASTORE_2: u8 = 0x4d; 84 | pub const ASTORE_3: u8 = 0x4e; 85 | pub const IASTORE: u8 = 0x4f; 86 | pub const LASTORE: u8 = 0x50; 87 | pub const FASTORE: u8 = 0x51; 88 | pub const DASTORE: u8 = 0x52; 89 | pub const AASTORE: u8 = 0x53; 90 | pub const BASTORE: u8 = 0x54; 91 | pub const CASTORE: u8 = 0x55; 92 | pub const SASTORE: u8 = 0x56; 93 | pub const POP: u8 = 0x57; 94 | pub const POP2: u8 = 0x58; 95 | pub const DUP: u8 = 0x59; 96 | pub const DUP_X1: u8 = 0x5a; 97 | pub const DUP_X2: u8 = 0x5b; 98 | pub const DUP2: u8 = 0x5c; 99 | pub const DUP2_X1: u8 = 0x5d; 100 | pub const DUP2_X2: u8 = 0x5e; 101 | pub const SWAP: u8 = 0x5f; 102 | pub const IADD: u8 = 0x60; 103 | pub const LADD: u8 = 0x61; 104 | pub const FADD: u8 = 0x62; 105 | pub const DADD: u8 = 0x63; 106 | pub const ISUB: u8 = 0x64; 107 | pub const LSUB: u8 = 0x65; 108 | pub const FSUB: u8 = 0x66; 109 | pub const DSUB: u8 = 0x67; 110 | pub const IMUL: u8 = 0x68; 111 | pub const LMUL: u8 = 0x69; 112 | pub const FMUL: u8 = 0x6a; 113 | pub const DMUL: u8 = 0x6b; 114 | pub const IDIV: u8 = 0x6c; 115 | pub const LDIV: u8 = 0x6d; 116 | pub const FDIV: u8 = 0x6e; 117 | pub const DDIV: u8 = 0x6f; 118 | pub const IREM: u8 = 0x70; 119 | pub const LREM: u8 = 0x71; 120 | pub const FREM: u8 = 0x72; 121 | pub const DREM: u8 = 0x73; 122 | pub const INEG: u8 = 0x74; 123 | pub const LNEG: u8 = 0x75; 124 | pub const FNEG: u8 = 0x76; 125 | pub const DNEG: u8 = 0x77; 126 | pub const ISHL: u8 = 0x78; 127 | pub const LSHL: u8 = 0x79; 128 | pub const ISHR: u8 = 0x7a; 129 | pub const LSHR: u8 = 0x7b; 130 | pub const IUSHR: u8 = 0x7c; 131 | pub const LUSHR: u8 = 0x7d; 132 | pub const IAND: u8 = 0x7e; 133 | pub const LAND: u8 = 0x7f; 134 | pub const IOR: u8 = 0x80; 135 | pub const LOR: u8 = 0x81; 136 | pub const IXOR: u8 = 0x82; 137 | pub const LXOR: u8 = 0x83; 138 | pub const IINC: u8 = 0x84; 139 | pub const I2L: u8 = 0x85; 140 | pub const I2F: u8 = 0x86; 141 | pub const I2D: u8 = 0x87; 142 | pub const L2I: u8 = 0x88; 143 | pub const L2F: u8 = 0x89; 144 | pub const L2D: u8 = 0x8a; 145 | pub const F2I: u8 = 0x8b; 146 | pub const F2L: u8 = 0x8c; 147 | pub const F2D: u8 = 0x8d; 148 | pub const D2I: u8 = 0x8e; 149 | pub const D2L: u8 = 0x8f; 150 | pub const D2F: u8 = 0x90; 151 | pub const I2B: u8 = 0x91; 152 | pub const I2C: u8 = 0x92; 153 | pub const I2S: u8 = 0x93; 154 | pub const LCMP: u8 = 0x94; 155 | pub const FCMPL: u8 = 0x95; 156 | pub const FCMPG: u8 = 0x96; 157 | pub const DCMPL: u8 = 0x97; 158 | pub const DCMPG: u8 = 0x98; 159 | pub const IFEQ: u8 = 0x99; 160 | pub const IFNE: u8 = 0x9a; 161 | pub const IFLT: u8 = 0x9b; 162 | pub const IFGE: u8 = 0x9c; 163 | pub const IFGT: u8 = 0x9d; 164 | pub const IFLE: u8 = 0x9e; 165 | pub const IF_ICMPEQ: u8 = 0x9f; 166 | pub const IF_ICMPNE: u8 = 0xa0; 167 | pub const IF_ICMPLT: u8 = 0xa1; 168 | pub const IF_ICMPGE: u8 = 0xa2; 169 | pub const IF_ICMPGT: u8 = 0xa3; 170 | pub const IF_ICMPLE: u8 = 0xa4; 171 | pub const IF_ACMPEQ: u8 = 0xa5; 172 | pub const IF_ACMPNE: u8 = 0xa6; 173 | pub const GOTO: u8 = 0xa7; 174 | pub const JSR: u8 = 0xa8; 175 | pub const RET: u8 = 0xa9; 176 | pub const TABLESWITCH: u8 = 0xaa; 177 | pub const LOOKUPSWITCH: u8 = 0xab; 178 | pub const IRETURN: u8 = 0xac; 179 | pub const LRETURN: u8 = 0xad; 180 | pub const FRETURN: u8 = 0xae; 181 | pub const DRETURN: u8 = 0xaf; 182 | pub const ARETURN: u8 = 0xb0; 183 | pub const RETURN: u8 = 0xb1; 184 | pub const GETSTATIC: u8 = 0xb2; 185 | pub const PUTSTATIC: u8 = 0xb3; 186 | pub const GETFIELD: u8 = 0xb4; 187 | pub const PUTFIELD: u8 = 0xb5; 188 | pub const INVOKEVIRTUAL: u8 = 0xb6; 189 | pub const INVOKESPECIAL: u8 = 0xb7; 190 | pub const INVOKESTATIC: u8 = 0xb8; 191 | pub const INVOKEINTERFACE: u8 = 0xb9; 192 | pub const INVOKEDYNAMIC: u8 = 0xba; 193 | pub const NEW: u8 = 0xbb; 194 | pub const NEWARRAY: u8 = 0xbc; 195 | pub const ANEWARRAY: u8 = 0xbd; 196 | pub const ARRAYLENGTH: u8 = 0xbe; 197 | pub const ATHROW: u8 = 0xbf; 198 | pub const CHECKCAST: u8 = 0xc0; 199 | pub const INSTANCEOF: u8 = 0xc1; 200 | pub const MONITORENTER: u8 = 0xc2; 201 | pub const MONITOREXIT: u8 = 0xc3; 202 | pub const WIDE: u8 = 0xc4; 203 | pub const MULTIANEWARRAY: u8 = 0xc5; 204 | pub const IFNULL: u8 = 0xc6; 205 | pub const IFNONNULL: u8 = 0xc7; 206 | pub const GOTO_W: u8 = 0xc8; 207 | pub const JSR_W: u8 = 0xc9; 208 | pub const BREAKPOINT: u8 = 0xca; 209 | pub const IMPDEP1: u8 = 0xfe; 210 | pub const IMPDEP2: u8 = 0xff; 211 | } 212 | -------------------------------------------------------------------------------- /src/vm/class.rs: -------------------------------------------------------------------------------- 1 | //! Internal JVM representations of classes and methods. 2 | 3 | use std::cell::RefCell; 4 | use std::collections::{HashMap, HashSet}; 5 | use std::rc::Rc; 6 | 7 | use model::class_file::{access_flags, ClassFile, constant_pool_index, MethodInfo}; 8 | use model::class_file::attribute::{AttributeInfo, ExceptionTableEntry}; 9 | use util::one_indexed_vec::OneIndexedVec; 10 | use vm::{native, sig, symref}; 11 | use vm::class_loader::ClassLoader; 12 | use vm::constant_pool::RuntimeConstantPool; 13 | use vm::frame::Frame; 14 | use vm::value::Value; 15 | 16 | /// A JVM representation of a class that has been loaded. 17 | #[derive(Debug)] 18 | pub struct Class { 19 | /// A symbolic reference to the class, comprised of its name (if a scalar type) or element type 20 | /// (if an array class). 21 | pub symref: symref::Class, 22 | /// The access flags for the class. 23 | pub access_flags: u16, 24 | /// The superclass extended by the class. If the class is `java/lang/Object`, this is `None`. 25 | pub superclass: Option>, 26 | /// The runtime constant pool of the current class, created from the constant pool defined in 27 | /// the `.class` file that has been loaded. 28 | constant_pool: RuntimeConstantPool, 29 | /// The fields of this class mapped to their access flags. This map includes both `static` and 30 | /// non-`static` fields. We don't separate them because it makes it easier to throw the correct 31 | /// runtime `Error` when certain invalid conditions are detected. 32 | fields: HashMap, 33 | /// The constants which populate the `static final` fields of this class. We don't immediately 34 | /// put these values into `class_fields` because they can include `String` literals, and we may 35 | /// not have loaded the `String` class yet. (This is also consistent with the spec, which 36 | /// states that these constants are set at class initialization time.) 37 | field_constants: HashMap, 38 | /// The methods of the class, mapped to their method structures. 39 | methods: HashMap, 40 | /// The values of the static fields of this class. These are only set at class initialization. 41 | /// §5.5 of the JVM spec requires that initialization occur only at certain specific points, 42 | /// in particular: 43 | /// * When an instance of the class is created with the `new` instruction 44 | /// * When one of the `getstatic`, `putstatic`, or `invokestatic` instructions refers to one 45 | /// of the static members of the class 46 | /// * When a subclass of the class is initialized 47 | /// * When the VM is about to begin executing the `main` method in the class containing the 48 | /// overall program entry point 49 | /// Prior to initialization, this structure field contains `None`. After initialization, this 50 | /// field contains a `Some` with a `HashMap` value, which must contain the current values for 51 | /// each `static` field of this class. 52 | field_values: RefCell>>, 53 | } 54 | 55 | impl Class { 56 | pub fn new(symref: symref::Class, superclass: Option>, 57 | constant_pool: RuntimeConstantPool, class_file: ClassFile) -> Self { 58 | let mut fields = HashMap::new(); 59 | let mut field_constants = HashMap::new(); 60 | for field_info in class_file.fields { 61 | let name = constant_pool.lookup_raw_string(field_info.name_index); 62 | let ty = sig::Type::new(&constant_pool.lookup_raw_string(field_info.descriptor_index)); 63 | let sig = sig::Field { name: name, ty: ty }; 64 | if field_info.access_flags & access_flags::field_access_flags::ACC_STATIC != 0 { 65 | for attribute in field_info.attributes { 66 | if let AttributeInfo::ConstantValue { constant_value_index } = attribute { 67 | field_constants.insert(sig.clone(), constant_value_index); 68 | } 69 | } 70 | } 71 | fields.insert(sig, field_info.access_flags); 72 | } 73 | 74 | let mut methods = HashMap::new(); 75 | for method_info in class_file.methods { 76 | let name = constant_pool.lookup_raw_string(method_info.name_index); 77 | let descriptor = constant_pool.lookup_raw_string(method_info.descriptor_index); 78 | let sig = sig::Method::new(&name, &descriptor); 79 | let method_symref = symref::Method { class: symref.clone(), sig: sig.clone() }; 80 | methods.insert(sig, Method::new(method_symref, method_info)); 81 | } 82 | 83 | Class { 84 | symref: symref, 85 | access_flags: class_file.access_flags, 86 | superclass: superclass, 87 | constant_pool: constant_pool, 88 | fields: fields, 89 | field_constants: field_constants, 90 | methods: methods, 91 | field_values: RefCell::new(None), 92 | } 93 | } 94 | 95 | /// Create a new array class for a given element type. 96 | pub fn new_array(object_class: Rc, component_access_flags: u16, 97 | component_type: sig::Type) -> Self { 98 | let access_flags = (component_access_flags & 0x0001) | 0x1030; 99 | let length_field = sig::Field { 100 | name: String::from("length"), 101 | ty: sig::Type::Int, 102 | }; 103 | let empty_constant_pool = OneIndexedVec::from(vec![]); 104 | let mut fields = HashMap::new(); 105 | fields.insert(length_field, 0x1011); 106 | Class { 107 | symref: symref::Class { sig: sig::Class::Array(Box::new(component_type)) }, 108 | access_flags: access_flags, 109 | superclass: Some(object_class.clone()), 110 | constant_pool: RuntimeConstantPool::new(&empty_constant_pool), 111 | fields: fields, 112 | field_constants: HashMap::new(), 113 | methods: HashMap::new(), 114 | field_values: RefCell::new(None), 115 | } 116 | } 117 | 118 | pub fn get_symref(&self) -> symref::Class { 119 | self.symref.clone() 120 | } 121 | 122 | pub fn get_access_flags(&self) -> u16 { 123 | self.access_flags 124 | } 125 | 126 | pub fn get_constant_pool(&self) -> &RuntimeConstantPool { 127 | &self.constant_pool 128 | } 129 | 130 | /// Find the method in the current class referred to by a given symbolic reference. If the 131 | /// method is not found, panics with a `NoSuchMethodError`. 132 | pub fn resolve_method(&self, method_symref: &symref::Method) -> &Method { 133 | // TODO access control 134 | // TODO check if this is an interface 135 | self.find_method(&method_symref.sig).expect("NoSuchMethodError") 136 | } 137 | 138 | /// Implements dynamic lookup of a method's signature in the current class. If no method with 139 | /// the given signature is found, then recursively searches the current class's superclasses. 140 | pub fn find_method(&self, method_sig: &sig::Method) -> Option<&Method> { 141 | self.methods.get(method_sig).or_else(|| { 142 | self.superclass.as_ref().and_then(|superclass| superclass.find_method(method_sig)) 143 | }) 144 | } 145 | 146 | /// Implements dynamic dispatch of a resolved method according to the lookup procedure 147 | /// specified for the `invokevirtual` instruction. Method resolution depends on whether the 148 | /// method in question overrides a superclass method. (See spec for more information.) 149 | pub fn dispatch_method(&self, resolved_method: &Method) -> Option<(&Class, &Method)> { 150 | self.methods.get(&resolved_method.symref.sig).and_then(|our_method| { 151 | if our_method.access_flags & access_flags::method_access_flags::ACC_PRIVATE != 0 152 | || our_method.access_flags & access_flags::method_access_flags::ACC_STATIC != 0 { 153 | None 154 | } else if resolved_method.access_flags & access_flags::method_access_flags::ACC_PUBLIC == 0 155 | && resolved_method.access_flags & access_flags::method_access_flags::ACC_PROTECTED == 0 156 | && resolved_method.access_flags & access_flags::method_access_flags::ACC_PRIVATE == 0 { 157 | // the resolved method is declared as package-private 158 | if self.symref.sig.get_package() == resolved_method.symref.class.sig.get_package() { 159 | Some((self, our_method)) 160 | } else { 161 | None 162 | } 163 | } else { 164 | Some((self, our_method)) 165 | } 166 | }).or_else({|| 167 | self.superclass.as_ref().and_then(|superclass| superclass.dispatch_method(resolved_method)) 168 | }) 169 | } 170 | 171 | /// Returns true if this class is a descendant (direct or indirect subclass) of another class. 172 | pub fn is_descendant(&self, other: &Class) -> bool { 173 | if self.symref.sig == other.symref.sig { 174 | true 175 | } else { 176 | self.superclass.as_ref().map_or(false, |superclass| { 177 | superclass.is_descendant(other) 178 | }) 179 | } 180 | } 181 | 182 | /// Initialize the class by executing its class or interface initialization method. Prior to 183 | /// initialization, a class or interface must be linked, that is, verified, prepared, and 184 | /// optionally resolved. 185 | pub fn initialize(&self, class_loader: &mut ClassLoader) { 186 | // we don't want to have the RefCell borrowed during the initializer 187 | // therefore, we borrow it in an inner scope and run the initializer later 188 | let run_initializer = { 189 | let mut field_values = self.field_values.borrow_mut(); 190 | match *field_values { 191 | None => { 192 | let mut map = HashMap::new(); 193 | 194 | // initialize all static fields to their default values 195 | for (sig, access_flags) in &self.fields { 196 | if access_flags & access_flags::field_access_flags::ACC_STATIC != 0 { 197 | let default_value = sig.ty.default_value(); 198 | map.insert(sig.clone(), default_value); 199 | } 200 | } 201 | 202 | // initialize fields with a ConstantValue attribute to those constant values 203 | for (sig, index) in &self.field_constants { 204 | let value = self.constant_pool.resolve_literal(*index, class_loader).unwrap(); 205 | map.insert(sig.clone(), value); 206 | } 207 | 208 | *field_values = Some(map); 209 | true 210 | }, 211 | 212 | Some(_) => false, 213 | } 214 | }; 215 | 216 | if run_initializer { 217 | let clinit_sig = sig::Method { 218 | name: String::from(""), 219 | params: vec![], 220 | return_ty: None, 221 | }; 222 | match self.methods.get(&clinit_sig) { 223 | None => (), 224 | Some(ref method) => { 225 | let result = method.invoke(&self, class_loader, vec![]); 226 | match result { 227 | None => (), 228 | Some(_) => panic!(" returned a value!"), 229 | } 230 | }, 231 | }; 232 | } 233 | } 234 | 235 | /// Resolves a symbolic reference to a field and reads a value from that field. 236 | pub fn resolve_and_get_field(&self, symref: &symref::Field, class_loader: &mut ClassLoader) 237 | -> Value { 238 | self.initialize(class_loader); 239 | // TODO we're ignoring the superinterfaces 240 | // TODO: also not checking for static 241 | let field_values_opt = self.field_values.borrow(); 242 | let field_values = field_values_opt.as_ref().unwrap(); 243 | let value_opt = field_values.get(&symref.sig).map(|v| v.clone()); 244 | value_opt.unwrap_or_else(move || { 245 | let superclass = self.superclass.as_ref().expect("NoSuchFieldError"); 246 | superclass.resolve_and_get_field(symref, class_loader) 247 | }) 248 | } 249 | 250 | /// Resolves a symbolic reference to a field and writes a new value to that field. 251 | pub fn resolve_and_put_field(&self, symref: &symref::Field, new_value: Value, 252 | class_loader: &mut ClassLoader) { 253 | self.initialize(class_loader); 254 | // TODO we're ignoring superinterfaces and not checking for static 255 | let mut field_values_opt = self.field_values.borrow_mut(); 256 | let mut field_values = field_values_opt.as_mut().unwrap(); 257 | if field_values.contains_key(&symref.sig) { 258 | field_values.insert(symref.sig.clone(), new_value); 259 | } else { 260 | let superclass = self.superclass.as_ref().expect("NoSuchFieldError"); 261 | superclass.resolve_and_put_field(symref, new_value, class_loader); 262 | } 263 | } 264 | 265 | /// Returns a set of the signatures of the fields of an instance of this class. 266 | pub fn collect_instance_fields(&self) -> HashSet { 267 | // TODO: fix semantics wrt private fields (which aren't inherited, but still exist in a 268 | // class object) 269 | let mut instance_fields = self.superclass.as_ref().map(|superclass| { 270 | superclass.collect_instance_fields() 271 | }).unwrap_or(HashSet::new()); 272 | for (sig, access_flags) in &self.fields { 273 | if access_flags & access_flags::field_access_flags::ACC_STATIC == 0 { 274 | instance_fields.insert(sig.clone()); 275 | } 276 | } 277 | instance_fields 278 | } 279 | } 280 | 281 | #[derive(Debug)] 282 | /// A JVM representation of a method in a loaded class. 283 | pub struct Method { 284 | /// The method's signature, comprised of its name and argument and return types. 285 | pub symref: symref::Method, 286 | /// The method's access flags. 287 | pub access_flags: u16, 288 | /// A `MethodCode` variant, which is used to actually invoke the method. 289 | code: MethodCode, 290 | } 291 | 292 | impl Method { 293 | pub fn new(symref: symref::Method, method_info: MethodInfo) -> Self { 294 | let method_code = { 295 | if method_info.access_flags & access_flags::method_access_flags::ACC_NATIVE != 0 { 296 | match native::bind(&symref) { 297 | None => MethodCode::NativeNotFound, 298 | Some(native_method) => MethodCode::Native(native_method), 299 | } 300 | } else if method_info.access_flags & access_flags::method_access_flags::ACC_ABSTRACT != 0 { 301 | MethodCode::Abstract 302 | } else { 303 | method_info.attributes.into_iter().fold(None, |method_code, attribute_info| { 304 | method_code.or( 305 | match attribute_info { 306 | AttributeInfo::Code { max_locals, code, exception_table, .. } => { 307 | Some(MethodCode::Concrete { 308 | max_locals: max_locals, 309 | code: code, 310 | exception_table: exception_table, 311 | }) 312 | }, 313 | _ => None, 314 | } 315 | ) 316 | }).unwrap() 317 | } 318 | }; 319 | Method { 320 | symref: symref, 321 | access_flags: method_info.access_flags, 322 | code: method_code, 323 | } 324 | } 325 | 326 | pub fn invoke(&self, class: &Class, class_loader: &mut ClassLoader, 327 | args: Vec) -> Option { 328 | println!("Starting to invoke {:?}", self); 329 | let result = match self.code { 330 | MethodCode::Concrete { max_locals, ref code, .. } => { 331 | let mut locals = Vec::with_capacity(max_locals as usize); 332 | for value in args { 333 | let realign = match value { 334 | Value::Long(_) | Value::Double(_) => true, 335 | _ => false, 336 | }; 337 | locals.push(Some(value)); 338 | if realign { 339 | locals.push(None); 340 | } 341 | } 342 | while locals.len() < max_locals as usize { 343 | locals.push(None) 344 | } 345 | let frame = Frame::new(class, code, locals); 346 | frame.run(class_loader) 347 | }, 348 | MethodCode::Abstract => panic!("AbstractMethodError"), 349 | MethodCode::Native(ref native_method) => native_method.invoke(args), 350 | MethodCode::NativeNotFound => panic!("UnsatisfiedLinkError"), 351 | }; 352 | println!("Finished invoking {:?}", self); 353 | result 354 | } 355 | } 356 | 357 | /// A representation of the code associated with a method, or more generally, the action that 358 | /// should be taken when a method is invoked. 359 | #[derive(Debug)] 360 | enum MethodCode { 361 | /// The code for a non-`abstract`, non-`native` Java method. Such contains executable bytecode 362 | /// which may be used to create a new JVM stack frame. 363 | Concrete { max_locals: u16, code: Vec, exception_table: Vec, }, 364 | /// to invoke an `abstract` method fails with `AbstractMethodError`. 365 | Abstract, 366 | /// The code for a `native` Java method for which the class loader has located a corresponding 367 | /// Rust function pointer. 368 | Native(native::NativeMethod), 369 | /// The code for a `native` Java method for which the class loader failed to locate a Rust 370 | /// function pointer. 371 | NativeNotFound, 372 | } 373 | -------------------------------------------------------------------------------- /src/vm/class_loader.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fmt}; 2 | use std::fs::File; 3 | use std::io::{self, Read}; 4 | use std::collections::{HashMap, HashSet}; 5 | use std::rc::Rc; 6 | 7 | use nom; 8 | 9 | use model::class_file::ClassFile; 10 | use parser::class_file; 11 | use vm::{sig, symref}; 12 | use vm::class; 13 | use vm::constant_pool::{RuntimeConstantPool, RuntimeConstantPoolEntry}; 14 | 15 | #[derive(Debug)] 16 | pub enum Error { 17 | /// If no "purported representation" of the class is found. §5.3.1. 18 | ClassNotFound { name: String, error: io::Error }, 19 | /// The "purported representation" does not follow the class file format. §5.3.5. 20 | ClassFormat, 21 | /// The "purported representation" is not of a supported version. §5.3.5. 22 | UnsupportedVersion { major: u16, minor: u16 }, 23 | /// The "purported representation" does not actually represent the requested class. §5.3.5. 24 | NoClassDefFound { name: String }, 25 | /// (A subtlety here is that recursive class loading to load superclasses is performed as part 26 | /// of resolution (§5.3.5, step 3). Therefore, a ClassNotFoundException that results from a 27 | /// class loader failing to load a superclass must be wrapped in a NoClassDefFoundError.) §5.3 28 | NoClassDefFoundCause { name: String, not_found: String }, 29 | IncompatibleClassChange(String), 30 | /// The class is its own superclass or superinterface. §5.3.5. 31 | ClassCircularity, 32 | } 33 | 34 | impl fmt::Display for Error { 35 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 | match *self { 37 | Error::ClassNotFound { ref name, ref error } => write!(f, "ClassNotFound: {}. {:?}", 38 | name, error), 39 | Error::ClassFormat => write!(f, "ClassFormat"), 40 | Error::UnsupportedVersion { major, minor } => 41 | write!(f, "UnsupportedVersion {}.{}", major, minor), 42 | Error::NoClassDefFound { ref name } => write!(f, "NoClassDefFound: {}", name), 43 | Error::NoClassDefFoundCause { ref name, ref not_found } => 44 | write!(f, "NoClassDefFound: {}. Caused by ClassNotFound: {}", name, not_found), 45 | Error::IncompatibleClassChange(ref class) => 46 | write!(f, "IncompatibleClassChange with {}", class), 47 | Error::ClassCircularity => write!(f, "ClassCircularity"), 48 | } 49 | } 50 | } 51 | 52 | impl error::Error for Error { 53 | fn description(&self) -> &str { 54 | match *self { 55 | Error::ClassNotFound { .. } => "class representation not found due to I/O error", 56 | Error::ClassFormat => "invalid class format", 57 | Error::UnsupportedVersion { .. } => "unsupported version", 58 | Error::NoClassDefFound { .. } => "class representation is not of the requested class", 59 | Error::NoClassDefFoundCause { .. } => 60 | "no class definition because a superclass is not found", 61 | Error::IncompatibleClassChange(_) => 62 | "declared superclass (superinterface) is actually an interface (class)", 63 | Error::ClassCircularity => "the class is its own superclass or superinterface", 64 | } 65 | } 66 | 67 | fn cause(&self) -> Option<&error::Error> { 68 | match *self { 69 | Error::ClassNotFound { ref error, .. } => Some(error), 70 | _ => None, 71 | } 72 | } 73 | } 74 | 75 | #[derive(Debug)] 76 | /// A class loader suitable for loading classes into the JVM. 77 | pub struct ClassLoader { 78 | /// The classes that have already been resolved by this class loader. 79 | classes: HashMap>, 80 | /// The signatures of classes that have not yet been resolved by this class loader. 81 | pending: HashSet, 82 | } 83 | 84 | impl ClassLoader { 85 | pub fn new() -> ClassLoader { 86 | ClassLoader { 87 | classes: HashMap::new(), 88 | pending: HashSet::new(), 89 | } 90 | } 91 | 92 | /// Given a class name, read the bytes from the corresponding class file. 93 | fn find_class_bytes(&mut self, name: &str) -> Result, io::Error> { 94 | // isn't this so convenient! 95 | // FIXME: Set up classpath for find_class_bytes 96 | let file_name = String::from("rt/") + name + ".class"; 97 | File::open(file_name).and_then(|mut file| { 98 | let mut res = vec![]; 99 | file.read_to_end(&mut res).map(|_| res) 100 | }) 101 | } 102 | 103 | /// Get the symbolic reference to a class from a runtime constant pool index. 104 | fn get_class_ref(rcp: &RuntimeConstantPool, index: u16)-> Result<&symref::Class, Error> { 105 | if let Some(RuntimeConstantPoolEntry::ClassRef(ref class_symref)) = rcp[index] { 106 | Ok(class_symref) 107 | } else { 108 | Err(Error::ClassFormat) 109 | } 110 | } 111 | 112 | /// Load a class based on a symbolic reference. 113 | pub fn resolve_class(&mut self, symref: &symref::Class) -> Result, Error> { 114 | // TODO check access modifiers 115 | self.load_class(&symref.sig) 116 | } 117 | 118 | /// Derives the super class (if it exists) of the specified class. 119 | fn derive_super_class(&mut self, rcp: &RuntimeConstantPool, class_file: &ClassFile) 120 | -> Result>, Error> { 121 | if class_file.super_class == 0 { 122 | Ok(None) 123 | } else { 124 | let super_symref = try!(Self::get_class_ref(rcp, class_file.super_class)); 125 | self.resolve_class(&super_symref).map(Some) 126 | } 127 | } 128 | 129 | /// Derives the specified class and its interfaces, but not its superclass. 130 | fn derive_class(&mut self, original_name: &str, sig: &sig::Class, class_bytes: &[u8]) 131 | -> Result, Error> { 132 | // TODO we discard the parse errors, but it's so hard to fix that... 133 | let parsed_class = try!( 134 | match class_file::parse_class_file(&class_bytes) { 135 | nom::IResult::Done(_, parsed_class) => Ok(parsed_class), 136 | nom::IResult::Incomplete(_) => Err(Error::ClassFormat), 137 | nom::IResult::Error(_) => Err(Error::ClassFormat), 138 | } 139 | ); 140 | try!( 141 | if parsed_class.major_version != 50 || parsed_class.minor_version != 0 { 142 | Err(Error::UnsupportedVersion { 143 | major: parsed_class.major_version, 144 | minor: parsed_class.minor_version, 145 | }) 146 | } else { 147 | Ok(()) 148 | } 149 | ); 150 | let rcp = RuntimeConstantPool::new(&parsed_class.constant_pool); 151 | let sig_matches = { 152 | let this_symref = try!(Self::get_class_ref(&rcp, parsed_class.this_class)); 153 | *sig == this_symref.sig 154 | }; 155 | if sig_matches { 156 | let super_class = try!(self.derive_super_class(&rcp, &parsed_class)); 157 | // TODO: Check that the entry is actually an interface 158 | for interface in &parsed_class.interfaces { 159 | let iface_symref = try!(Self::get_class_ref(&rcp, *interface)); 160 | try!(self.resolve_class(&iface_symref)); 161 | } 162 | let symref = symref::Class { sig: sig.clone() }; 163 | let class = class::Class::new(symref, super_class, rcp, parsed_class); 164 | let rc = Rc::new(class); 165 | self.classes.insert(sig.clone(), rc.clone()); 166 | Ok(rc) 167 | } else { 168 | Err(Error::NoClassDefFound { name: String::from(original_name) }) 169 | } 170 | } 171 | 172 | /// Attempts to create, load, and prepare the specified class from the specified bytes. The 173 | /// bootstrap class loader searches the current directory for a class file with the correct 174 | /// fully-qualified name. If none is found, the bootstrap class loader then attempts to load 175 | /// the class from the standard library JAR. 176 | /// 177 | /// This implementation lazily resolves symbolic references, so no resolution of references 178 | /// within the loaded class is performed by this function. 179 | /// 180 | /// This implementation does not attempt to perform bytecode verification; we assume that any 181 | /// class files we attempt to load are valid. 182 | fn load_class_bytes(&mut self, name: &str, sig: &sig::Class, class_bytes: &[u8]) 183 | -> Result, Error> { 184 | self.derive_class(name, sig, class_bytes) 185 | } 186 | 187 | /// Attempts to create, load, and prepare the specified class using the bootstrap class loader 188 | /// implementation. The bootstrap class loader searches the current directory for a class file 189 | /// with the correct fully-qualified name. If none is found, the bootstrap class loader then 190 | /// attempts to load the class from the standard library JAR. 191 | /// 192 | /// This implementation lazily resolves symbolic references, so no resolution of references 193 | /// within the loaded class is performed by this function. 194 | /// 195 | /// This implementation does not attempt to perform bytecode verification; we assume that any 196 | /// class files we attempt to load are valid. 197 | pub fn load_class(&mut self, sig: &sig::Class) -> Result, Error> { 198 | if self.pending.contains(&sig) { 199 | // we're already resolving this name 200 | return Err(Error::ClassCircularity) 201 | } else if let Some(class) = self.classes.get(&sig) { 202 | // the class is already resolved 203 | return Ok(class.clone()) 204 | } 205 | 206 | // this can't just be an else block thanks to the borrow checker 207 | self.pending.insert(sig.clone()); 208 | let res = match *sig { 209 | sig::Class::Scalar(ref name) => { 210 | let class_bytes = try!(self.find_class_bytes(name) 211 | .map_err(|e| Error::ClassNotFound { 212 | name: name.clone(), 213 | error: e, 214 | })); 215 | // TODO: Catch errors in recursive loading and wrap them in a NoClassDefFoundError 216 | // as specified in §5.3 217 | self.load_class_bytes(name, sig, class_bytes.as_slice()) 218 | }, 219 | 220 | sig::Class::Array(ref component_type) => { 221 | // load the component type class, even though we don't use it, to ensure that any 222 | // errors resulting from the load happen at the right time 223 | let component_access_flags = try!( 224 | match **component_type { 225 | sig::Type::Byte | sig::Type::Char | sig::Type::Double 226 | | sig::Type::Float | sig::Type::Int | sig::Type::Long 227 | | sig::Type::Short | sig::Type::Boolean => Ok(0x1031), 228 | sig::Type::Reference(ref component_sig) => 229 | self.load_class(component_sig).map(|class| class.get_access_flags()) 230 | } 231 | ); 232 | let object_name = String::from("java/lang/Object"); 233 | let object_sig = sig::Class::Scalar(object_name); 234 | let object_class = try!(self.load_class(&object_sig)); 235 | let class = class::Class::new_array(object_class, component_access_flags, 236 | *component_type.clone()); 237 | let rc = Rc::new(class); 238 | self.classes.insert(sig.clone(), rc.clone()); 239 | Ok(rc) 240 | }, 241 | }; 242 | self.pending.remove(&sig); 243 | res 244 | } 245 | } 246 | 247 | -------------------------------------------------------------------------------- /src/vm/constant_pool.rs: -------------------------------------------------------------------------------- 1 | //! The runtime constant pool. 2 | //! 3 | //! Near the beginning of every Java class file is a section known as the _constant pool_. Broadly 4 | //! speaking, this constant pool contains two types of values: _symbolic references_ and 5 | //! _literals_. Symbolic references are the names and signatures of classes, fields, and methods 6 | //! referred to in the file, which are _resolved_ into their runtime representations by the JVM. 7 | //! Literals can be of primitive types or `String` literals, which are stored in a format called 8 | //! "modified UTF-8". The constant pool is defined in §4.4 of the specification. 9 | //! 10 | //! One of the class loader's tasks is to construct a _runtime constant pool_ from the constant 11 | //! pool contained in the class file. This module contains the structures representing that runtime 12 | //! constant pool, which are the structures actually used when constant pool entries are referred 13 | //! to in Java bytecode. More information about the runtime constant pool is found in §5.1 of the 14 | //! specification. 15 | //! 16 | //! There are many instructions which refer to entries in the runtime constant pool. Any 17 | //! instruction which refers to a particular class, like the `new` instruction, refers to a 18 | //! symbolic reference in the runtime constant pool; any instruction referring to a field or method 19 | //! similarly also refers to a symbolic reference. In addition, the `ldc`, `ldc_w`, and `ldc2_w` 20 | //! instructions allow Java bytecode to directly load constant literals (and, if reflection were 21 | //! implemented, other constant pool entries as well) onto the stack for manipulation by the 22 | //! program. 23 | 24 | use std::cell::RefCell; 25 | use std::num::Wrapping; 26 | use std::ops::Index; 27 | use std::rc::Rc; 28 | 29 | use model::class_file::constant_pool::{ConstantPool, ConstantPoolInfo}; 30 | use util::one_indexed_vec::OneIndexedVec; 31 | use vm::{sig, symref}; 32 | use vm::class_loader::{self, ClassLoader}; 33 | use vm::value::{Array, Scalar, Value}; 34 | 35 | pub use model::class_file::constant_pool::constant_pool_index; 36 | 37 | #[derive(Debug)] 38 | /// An constant value in the runtime constant pool. 39 | pub enum RuntimeConstantPoolEntry { 40 | /// A symbolic reference to a class. 41 | ClassRef(symref::Class), 42 | /// A symbolic reference to a method. 43 | MethodRef(symref::Method), 44 | /// A symbolic reference to an object field. 45 | FieldRef(symref::Field), 46 | /// A literal value that has undergone resolution. 47 | ResolvedLiteral(Value), 48 | /// An unresolved reference to a modified UTF-8 string in the constant pool. 49 | UnresolvedString(constant_pool_index), 50 | /// A resolved modified UTF-8 string value. 51 | StringValue(ModifiedUtf8String), 52 | } 53 | 54 | #[derive(Debug)] 55 | /// A runtime constant pool. This just consists of a `OneIndexedVec` of constant pool entries. 56 | pub struct RuntimeConstantPool { 57 | entries: OneIndexedVec>, 58 | } 59 | 60 | impl Index for RuntimeConstantPool { 61 | type Output = Option; 62 | 63 | fn index(&self, index: constant_pool_index) -> &Self::Output { 64 | &self.entries[index as usize] 65 | } 66 | } 67 | 68 | impl RuntimeConstantPool { 69 | /// Creates a new runtime constant pool from the `ConstantPool` returned by the class file 70 | /// parser. Most of this process involves constructing `sig` and `symref` structures 71 | /// representing the symbolic references in the constant pool. 72 | pub fn new(constant_pool: &ConstantPool) -> Self { 73 | let mut entries = vec![]; 74 | for info in constant_pool { 75 | let entry = match *info { 76 | ConstantPoolInfo::Class { .. } => { 77 | let class_symref = Self::force_class_ref(&constant_pool, &info); 78 | Some(RuntimeConstantPoolEntry::ClassRef(class_symref)) 79 | }, 80 | 81 | ConstantPoolInfo::FieldRef { class_index, name_and_type_index } => { 82 | let class_symref = 83 | Self::force_class_ref(&constant_pool, 84 | &constant_pool[class_index as usize]); 85 | let (name, descriptor) = 86 | Self::force_name_and_type(&constant_pool, 87 | &constant_pool[name_and_type_index as usize]); 88 | let ty = sig::Type::new(&descriptor); 89 | let sig = sig::Field { name: name, ty: ty }; 90 | let field_symref = symref::Field { class: class_symref, sig: sig }; 91 | Some(RuntimeConstantPoolEntry::FieldRef(field_symref)) 92 | }, 93 | 94 | ConstantPoolInfo::MethodRef { class_index, name_and_type_index } => { 95 | let class_symref = 96 | Self::force_class_ref(&constant_pool, &constant_pool[class_index as usize]); 97 | let (name, descriptor) = 98 | Self::force_name_and_type(&constant_pool, 99 | &constant_pool[name_and_type_index as usize]); 100 | let sig = sig::Method::new(&name, &descriptor); 101 | let method_symref = symref::Method { class: class_symref, sig: sig }; 102 | Some(RuntimeConstantPoolEntry::MethodRef(method_symref)) 103 | }, 104 | 105 | ConstantPoolInfo::String { string_index } => { 106 | Some(RuntimeConstantPoolEntry::UnresolvedString(string_index)) 107 | }, 108 | 109 | ConstantPoolInfo::Integer { bytes } => { 110 | let value = Value::Int(Wrapping(bytes as i32)); 111 | Some(RuntimeConstantPoolEntry::ResolvedLiteral(value)) 112 | }, 113 | 114 | ConstantPoolInfo::Float { bytes } => { 115 | let value = Value::Float(bytes as f32); 116 | Some(RuntimeConstantPoolEntry::ResolvedLiteral(value)) 117 | }, 118 | 119 | ConstantPoolInfo::Long { high_bytes, low_bytes } => { 120 | let bits = ((high_bytes as i64) << 32) & (low_bytes as i64); 121 | let value = Value::Long(Wrapping(bits)); 122 | Some(RuntimeConstantPoolEntry::ResolvedLiteral(value)) 123 | }, 124 | 125 | ConstantPoolInfo::Double { high_bytes, low_bytes } => { 126 | let bits = ((high_bytes as u64) << 32) & (low_bytes as u64); 127 | let value = Value::Double(bits as f64); 128 | Some(RuntimeConstantPoolEntry::ResolvedLiteral(value)) 129 | }, 130 | 131 | ConstantPoolInfo::NameAndType { .. } => None, 132 | 133 | ConstantPoolInfo::Utf8 { ref bytes } => { 134 | let modified_utf8 = ModifiedUtf8String::new(bytes.to_vec()); 135 | Some(RuntimeConstantPoolEntry::StringValue(modified_utf8)) 136 | }, 137 | 138 | ConstantPoolInfo::Unusable => None, 139 | 140 | _ => None, 141 | }; 142 | entries.push(entry); 143 | } 144 | RuntimeConstantPool { entries: OneIndexedVec::from(entries) } 145 | } 146 | 147 | /// Constructs a `symref::Class` from a `ConstantPoolInfo::Class`, panicking if `info` is of a 148 | /// different variant of `ConstantPoolInfo`. 149 | /// 150 | /// This should only be called where the specification requires that `info` be of the correct 151 | /// variant. 152 | fn force_class_ref(constant_pool: &ConstantPool, info: &ConstantPoolInfo) -> symref::Class { 153 | match *info { 154 | ConstantPoolInfo::Class { name_index } => { 155 | let name = Self::force_string(&constant_pool[name_index as usize]).to_string(); 156 | symref::Class { sig: sig::Class::new(&name) } 157 | }, 158 | _ => panic!("expected ConstantPoolInfo::Class"), 159 | } 160 | } 161 | 162 | /// Constructs a tuple of name and descriptor (type) strings from a 163 | /// `ConstantPoolInfo::NameAndType`, panicking if `info` is of a different variant of 164 | /// `ConstantPoolInfo`. The names of classes are binary names (§4.2.1) while the names of 165 | /// fields and methods are unqualified names (§4.2.2). Descriptor formats vary depending on the 166 | /// type of descriptor being referenced (§4.3). 167 | /// 168 | /// This should only be called where the specification requires that `info` be of the correct 169 | /// variant. 170 | fn force_name_and_type(constant_pool: &ConstantPool, info: &ConstantPoolInfo) 171 | -> (String, String) { 172 | match *info { 173 | ConstantPoolInfo::NameAndType { name_index, descriptor_index } => { 174 | let ref name_info = constant_pool[name_index as usize]; 175 | let ref descriptor_info = constant_pool[descriptor_index as usize]; 176 | let name_string = Self::force_string(name_info).to_string(); 177 | let descriptor_string = Self::force_string(descriptor_info).to_string(); 178 | (name_string, descriptor_string) 179 | }, 180 | _ => panic!("expected ConstantPoolInfo::NameAndType"), 181 | } 182 | } 183 | 184 | /// Constructs a `ModifiedUtf8String` from a `ConstantPoolInfo::Utf8`, panicking in `info` is 185 | /// of a different variant of `ConstantPoolInfo`. 186 | /// 187 | /// This should only be called where the specification requires that `info` be of the correct 188 | /// variant. 189 | fn force_string(info: &ConstantPoolInfo) -> ModifiedUtf8String { 190 | match *info { 191 | ConstantPoolInfo::Utf8 { ref bytes } => { 192 | ModifiedUtf8String::new(bytes.to_vec()) 193 | }, 194 | _ => panic!("expected ConstantPoolInfo::Utf8"), 195 | } 196 | } 197 | 198 | /// Returns the `String` at the runtime constant pool entry at `index`, panicking if that entry 199 | /// is not a `RuntimeConstantPoolEntry::StringValue`. This is used during class creation, 200 | /// because the structures describing fields and methods later in the class file (after the 201 | /// constant pool) use constant pool indices to refer to their names. 202 | pub fn lookup_raw_string(&self, index: constant_pool_index) -> String { 203 | match self.entries[index as usize] { 204 | Some(RuntimeConstantPoolEntry::StringValue(ref modified_utf8)) => 205 | modified_utf8.to_string(), 206 | _ => panic!("expected RuntimeConstantPoolInfo::StringValue"), 207 | } 208 | } 209 | 210 | /// Resolves a literal value in the constant pool into a `Value`. For `String` literals, this 211 | /// requires instantiating an instance of the `String` class, which we do by calling the 212 | /// `String(char[])` constructor using the content of the modified UTF-8 string in the constant 213 | /// pool, parsed into UTF-16. 214 | pub fn resolve_literal(&self, index: constant_pool_index, class_loader: &mut ClassLoader) 215 | -> Result { 216 | match self.entries[index as usize] { 217 | Some(RuntimeConstantPoolEntry::ResolvedLiteral(ref value)) => Ok(value.clone()), 218 | Some(RuntimeConstantPoolEntry::UnresolvedString(string_index)) => { 219 | let array_sig = sig::Class::Array(Box::new(sig::Type::Char)); 220 | let array_symref = symref::Class { sig: array_sig.clone() }; 221 | let array_class = try!(class_loader.resolve_class(&array_symref)); 222 | 223 | let chars = { 224 | if let Some(RuntimeConstantPoolEntry::StringValue(ref modified_utf8)) = 225 | self.entries[string_index as usize] { 226 | modified_utf8.to_utf16() 227 | } else { 228 | panic!("expected RuntimeConstantPoolEntry::StringValue"); 229 | } 230 | }; 231 | let mut array = Array::new(array_class, chars.len() as i32); 232 | let mut i = 0; 233 | for c in chars { 234 | array.put(i, Value::Int(Wrapping(c as i32))); 235 | i += 1; 236 | } 237 | let array_rc = Rc::new(RefCell::new(array)); 238 | 239 | let string_sig = sig::Class::Scalar(String::from("java/lang/String")); 240 | let string_symref = symref::Class { sig: string_sig }; 241 | let string_class = try!(class_loader.resolve_class(&string_symref)); 242 | let string = Scalar::new(string_class.clone()); 243 | let string_rc = Rc::new(RefCell::new(string)); 244 | 245 | let constructor_sig = sig::Method { 246 | name: String::from(""), 247 | params: vec![sig::Type::Reference(array_sig.clone())], 248 | return_ty: None, 249 | }; 250 | let constructor_symref = symref::Method { 251 | class: string_symref, 252 | sig: constructor_sig, 253 | }; 254 | let constructor = string_class.resolve_method(&constructor_symref); 255 | let args = vec![Value::ScalarReference(string_rc.clone()), 256 | Value::ArrayReference(array_rc)]; 257 | let result = constructor.invoke(string_class.as_ref(), class_loader, args); 258 | match result { 259 | None => (), 260 | Some(_) => panic!(" returned a value!"), 261 | } 262 | Ok(Value::ScalarReference(string_rc)) 263 | }, 264 | _ => panic!("expected literal constant pool entry"), 265 | } 266 | } 267 | } 268 | 269 | #[derive(Debug)] 270 | /// Represents a modified UTF-8 string (§4.4.7). This structure is created directly from the bytes 271 | /// in the class file, and has not undergone any kind of validation. 272 | pub struct ModifiedUtf8String { 273 | bytes: Vec, 274 | } 275 | 276 | impl ModifiedUtf8String { 277 | fn new(bytes: Vec) -> Self { 278 | ModifiedUtf8String { bytes: bytes } 279 | } 280 | 281 | /// Converts a modified UTF-8 string to a Rust `String`. 282 | fn to_string(&self) -> String { 283 | let mut utf8 = vec![]; 284 | let mut i = 0; 285 | while i < self.bytes.len() { 286 | match self.bytes[i] { 287 | 0x01 ... 0x7f => { 288 | utf8.push(self.bytes[i]); 289 | i += 1; 290 | }, 291 | 0xc0 ... 0xdf => { 292 | if self.bytes.len() < i + 2 { 293 | panic!("error decoding modified UTF-8: invalid sequence"); 294 | } else if self.bytes[i] == 0xc0 && self.bytes[i + 1] == 0x80 { 295 | // this is the encoding of a null character 296 | utf8.push(0x00); 297 | } else { 298 | utf8.push(self.bytes[i]); 299 | utf8.push(self.bytes[i + 1]); 300 | } 301 | i += 2; 302 | }, 303 | 0xe0 ... 0xef => { 304 | if self.bytes.len() < i + 3 { 305 | panic!("error decoding modified UTF-8: invalid sequence"); 306 | } else if self.bytes[i] == 0xed && self.bytes[i + 1] >= 0xa0 307 | && self.bytes[i + 1] <= 0xaf { 308 | // this sequence encodes a high surrogate 309 | // check that the following sequence encodes a low surrogate 310 | if self.bytes.len() < i + 6 || self.bytes[i + 3] != 0xed 311 | || self.bytes[i + 4] < 0xb0 || self.bytes[i + 4] > 0xbf { 312 | panic!("error decoding modified UTF-8: invalid surrogate pair"); 313 | } else { 314 | // decode the surrogate pair into a code point 315 | let code_point = (((self.bytes[i + 1] & 0x0f) as u32) << 16) 316 | & (((self.bytes[i + 2] & 0x3f) as u32) << 10) 317 | & (((self.bytes[i + 4] & 0x0f) as u32) << 6) 318 | & ((self.bytes[i + 5] & 0x3f) as u32) 319 | + 0x10000; 320 | // encode the code point in UTF-8 321 | utf8.push(0xf0 & ((code_point & 0x001c0000 >> 18) as u8)); 322 | utf8.push(0x80 & ((code_point & 0x0003f000 >> 12) as u8)); 323 | utf8.push(0x80 & ((code_point & 0x00000fc0 >> 6) as u8)); 324 | utf8.push(0x80 & ((code_point & 0x0000003f) as u8)); 325 | // skip past the entire surrogate pair 326 | i += 6; 327 | } 328 | } else { 329 | utf8.push(self.bytes[i]); 330 | utf8.push(self.bytes[i + 1]); 331 | utf8.push(self.bytes[i + 2]); 332 | i += 3; 333 | } 334 | }, 335 | 0x80 ... 0xbf => panic!("error decoding modified UTF-8: invalid continuation byte"), 336 | _ => panic!("error decoding modified UTF-8: illegal byte"), 337 | } 338 | } 339 | String::from_utf8(utf8).expect("unexpected error decoding modified UTF-8") 340 | } 341 | 342 | /// Converts a modified UTF-8 string to a UTF-16 string. This function is provided as an 343 | /// optimization in creating Java `String` literals, which are in UTF-16 format. It does not 344 | /// validate surrogate pairs. 345 | fn to_utf16(&self) -> Vec { 346 | let mut utf16 = vec![]; 347 | let mut i = 0; 348 | while i < self.bytes.len() { 349 | match self.bytes[i] { 350 | 0x01 ... 0x7f => { 351 | utf16.push(self.bytes[i] as u16); 352 | i += 1; 353 | }, 354 | 0xc0 ... 0xdf => { 355 | if self.bytes.len() < i + 2 { 356 | panic!("error decoding modified UTF-8: invalid sequence"); 357 | } else if self.bytes[i] == 0xc0 && self.bytes[i + 1] == 0x80 { 358 | // this is the encoding of a null character 359 | utf16.push(0x0000); 360 | } else { 361 | let code_point = 362 | (((self.bytes[i] & 0x1f) as u16) << 6) 363 | & ((self.bytes[i + 1] & 0x3f) as u16); 364 | utf16.push(code_point); 365 | } 366 | i += 2; 367 | }, 368 | 0xe0 ... 0xef => { 369 | if self.bytes.len() < i + 3 { 370 | panic!("error decoding modified UTF-8: invalid sequence"); 371 | } else { 372 | let code_point = 373 | (((self.bytes[i] & 0x0f) as u16) << 12) 374 | & (((self.bytes[i + 1] & 0x3f) as u16) << 6) 375 | & ((self.bytes[i + 2] & 0x3f) as u16); 376 | utf16.push(code_point); 377 | i += 3; 378 | } 379 | }, 380 | 0x80 ... 0xbf => panic!("error decoding modified UTF-8: invalid continuation byte"), 381 | _ => panic!("error decoding modified UTF-8: illegal byte"), 382 | } 383 | } 384 | utf16 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /src/vm/frame.rs: -------------------------------------------------------------------------------- 1 | //! The runtime state of a method that is currently executing in the virtual machine. 2 | //! 3 | //! Each thread in the Java virtual machine has a _runtime stack_ comprised of _frames_. A new 4 | //! frame is pushed onto the stack each time a method is invoked. Frames are removed from the stack 5 | //! either when the method of the current stack frame returns, or when an unhandled exception 6 | //! causes abrupt completion of the method (causing stack unwinding). 7 | //! 8 | //! Every stack frame has an array of bytecode instructions and a _program counter_ which define 9 | //! its execution. It also maintains a fixed number of _local variables_ and an _operand stack_. 10 | //! The operand stack is the central focus of the Java machine bytecode, and is directly 11 | //! manipulated by the bytecode instructions (in lieu of registers). 12 | 13 | use std::cell::RefCell; 14 | use std::num::Wrapping; 15 | use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Rem, Sub}; 16 | use std::rc::Rc; 17 | 18 | use model::class_file::access_flags::class_access_flags; 19 | 20 | use vm::{sig, symref}; 21 | use vm::bytecode::opcode; 22 | use vm::class::Class; 23 | use vm::class_loader::ClassLoader; 24 | use vm::constant_pool::RuntimeConstantPoolEntry; 25 | use vm::sig::Type; 26 | use vm::value::{Array, Scalar, Value}; 27 | 28 | /// A frame is used to store data and partial results, as well as to perform dynamic linking, 29 | /// return values for methods, and dispatch exceptions. 30 | #[derive(Debug)] 31 | pub struct Frame<'a> { 32 | /// A reference to the class containing the currently executing method. 33 | current_class: &'a Class, 34 | /// The bytecode currently executing in this frame. 35 | code: &'a [u8], 36 | /// The current program counter. 37 | pc: u16, 38 | /// The local variables of the current method. 39 | /// Values that occupy two indices (`long` and `double`) are stored in one slot followed by a 40 | /// `None` value in the subsequent index. 41 | local_variables: Vec>, 42 | /// The operand stack manipulated by the instructions of the current method. 43 | operand_stack: Vec, 44 | } 45 | 46 | impl<'a> Frame<'a> { 47 | pub fn new(current_class: &'a Class, code: &'a [u8], 48 | local_variables: Vec>) -> Self { 49 | Frame { 50 | current_class: current_class, 51 | code: code, 52 | pc: 0, 53 | local_variables: local_variables, 54 | operand_stack: vec![], 55 | } 56 | } 57 | 58 | /// Read a byte (`u8`) value and advance the program counter. 59 | fn read_next_byte(&mut self) -> u8 { 60 | let result = self.code[self.pc as usize]; 61 | self.pc += 1; 62 | result 63 | } 64 | 65 | /// Read a short (`u16`) value and advance the program counter by 2. 66 | fn read_next_short(&mut self) -> u16 { 67 | ((self.read_next_byte() as u16) << 8) | (self.read_next_byte() as u16) 68 | } 69 | 70 | /// Remove `count` items from the operand stack. 71 | fn pop_multi(&mut self, count: usize) -> Vec { 72 | let start_index = self.operand_stack.len() - count; 73 | self.operand_stack.drain(start_index..).collect() 74 | } 75 | 76 | /// Execute the method associated with this stack frame in the context of the currrent class 77 | /// loader, and return a result if there is one. This method may create new stack frames as a 78 | /// result of evaluating `invoke*` instructions. 79 | pub fn run(mut self, class_loader: &mut ClassLoader) -> Option { 80 | macro_rules! pop { 81 | () => (self.operand_stack.pop().unwrap()); 82 | ($value_variant: path) => ({ 83 | match pop!() { 84 | $value_variant(v) => v, 85 | v => panic!("Expected to pop a value of type {}, but was {:?}", 86 | stringify!($value_variant), v), 87 | } 88 | }); 89 | } 90 | 91 | macro_rules! pop_not_null { 92 | () => ({ 93 | match pop!() { 94 | Value::NullReference => panic!( 95 | "NullPointerException: expected {} but was null", 96 | stringify!($value_variant)), 97 | v => v, 98 | } 99 | }); 100 | ($value_variant: path) => ({ 101 | match pop!() { 102 | Value::NullReference => panic!( 103 | "NullPointerException: expected {} but was null", 104 | stringify!($value_variant)), 105 | $value_variant(v) => v, 106 | v => panic!("Expected to pop a value of type {}, but was {:?}", 107 | stringify!($value_variant), v), 108 | } 109 | }); 110 | } 111 | 112 | macro_rules! push { 113 | ($v: expr) => ({ 114 | let v = $v; // satisfy the borrow checker 115 | self.operand_stack.push(v); 116 | }); 117 | ($($vs: expr),*) => ({ 118 | self.operand_stack.extend_from_slice(&[$($vs),*]) 119 | }) 120 | } 121 | 122 | macro_rules! with { 123 | ($read_next_action: ident, $k: ident) => ({ 124 | let value = self.$read_next_action() as u16; 125 | $k!(value); 126 | }) 127 | } 128 | 129 | macro_rules! do_ipush { 130 | ($value: ident) => (push!(Value::Int(Wrapping($value as i32)))) 131 | } 132 | 133 | macro_rules! do_ldc { 134 | ($index: ident) => ({ 135 | let value = self.current_class.get_constant_pool() 136 | .resolve_literal($index, class_loader).unwrap(); 137 | push!(value); 138 | }); 139 | } 140 | 141 | macro_rules! do_load { 142 | ($index: expr) => ({ 143 | let value = self.local_variables[$index as usize].clone().unwrap(); 144 | push!(value); 145 | }) 146 | } 147 | 148 | macro_rules! do_store { 149 | ($index: expr) => ({ 150 | let value = self.operand_stack.pop().unwrap(); 151 | // invalidate the slot after this one if we're storing a category 2 operand 152 | match value { 153 | Value::Int(_) | Value::Float(_) | Value::ScalarReference(_) 154 | | Value::ArrayReference(_) | Value::NullReference => (), 155 | Value::Long(_) | Value::Double(_) => { 156 | self.local_variables[($index + 1) as usize] = None; 157 | }, 158 | } 159 | // actually store the local variable 160 | self.local_variables[$index as usize] = Some(value); 161 | // invalidate the slot before this one if it was formerly storing a category 2 162 | // operand 163 | let prev_index = $index - 1; 164 | if prev_index > 0 { 165 | match self.local_variables[prev_index as usize] { 166 | None | Some(Value::Int(_)) | Some(Value::Float(_)) 167 | | Some(Value::ScalarReference(_)) | Some(Value::ArrayReference(_)) 168 | | Some(Value::NullReference) => (), 169 | Some(Value::Long(_)) | Some(Value::Double(_)) => { 170 | self.local_variables[prev_index as usize] = None; 171 | }, 172 | } 173 | } 174 | }) 175 | } 176 | 177 | macro_rules! do_binop { 178 | ($value_variant: path, $binop: expr) => ({ 179 | let v2 = pop!($value_variant); 180 | let v1 = pop!($value_variant); 181 | push!($value_variant($binop(v1, v2))); 182 | }); 183 | } 184 | 185 | macro_rules! do_if_icmp { 186 | ($cmp_op: expr) => ({ 187 | let branch_offset = self.read_next_short() as i16; 188 | let i2 = pop!(Value::Int); 189 | let i1 = pop!(Value::Int); 190 | if $cmp_op(&i1, &i2) { 191 | // 3 byte long instruction; read* operations move the PC. 192 | let this_pc_start = self.pc - 3; 193 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16 194 | } 195 | }); 196 | } 197 | 198 | macro_rules! do_if_int { 199 | ($pred: expr) => ({ 200 | let branch_offset = self.read_next_short() as i16; 201 | let x = pop!(Value::Int); 202 | if $pred(&x, &Wrapping(0)) { 203 | // 3 byte long instruction; read* operations move the PC. 204 | let this_pc_start = self.pc - 3; 205 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16 206 | } 207 | }); 208 | } 209 | 210 | macro_rules! map_top { 211 | ($pat: pat, $result: expr) => ({ 212 | match pop!() { 213 | $pat => push!($result), 214 | v => panic!("Expected to pop a value of type {}, but was {:?}", 215 | stringify!($pat), v), 216 | } 217 | }); 218 | } 219 | 220 | loop { 221 | match self.read_next_byte() { 222 | opcode::NOP => (), 223 | opcode::ACONST_NULL => push!(Value::NullReference), 224 | opcode::ICONST_M1 => push!(Value::Int(Wrapping(-1))), 225 | opcode::ICONST_0 => push!(Value::Int(Wrapping(0))), 226 | opcode::ICONST_1 => push!(Value::Int(Wrapping(1))), 227 | opcode::ICONST_2 => push!(Value::Int(Wrapping(2))), 228 | opcode::ICONST_3 => push!(Value::Int(Wrapping(3))), 229 | opcode::ICONST_4 => push!(Value::Int(Wrapping(4))), 230 | opcode::ICONST_5 => push!(Value::Int(Wrapping(5))), 231 | opcode::LCONST_0 => push!(Value::Long(Wrapping(0))), 232 | opcode::LCONST_1 => push!(Value::Long(Wrapping(1))), 233 | opcode::FCONST_0 => push!(Value::Float(0.0)), 234 | opcode::FCONST_1 => push!(Value::Float(1.0)), 235 | opcode::FCONST_2 => push!(Value::Float(2.0)), 236 | opcode::DCONST_0 => push!(Value::Double(0.0)), 237 | opcode::DCONST_1 => push!(Value::Double(1.0)), 238 | opcode::BIPUSH => with!(read_next_byte, do_ipush), 239 | opcode::SIPUSH => with!(read_next_short, do_ipush), 240 | opcode::LDC => with!(read_next_byte, do_ldc), 241 | opcode::LDC_W | opcode::LDC2_W => with!(read_next_short, do_ldc), 242 | 243 | // these are a little out of order in order to combine similar cases 244 | opcode::ILOAD | opcode::LLOAD | opcode::FLOAD | opcode::DLOAD | opcode::ALOAD => 245 | with!(read_next_byte, do_load), 246 | opcode::ILOAD_0 | opcode::LLOAD_0 | opcode::FLOAD_0 | opcode::DLOAD_0 247 | | opcode::ALOAD_0 => 248 | do_load!(0), 249 | opcode::ILOAD_1 | opcode::LLOAD_1 | opcode::FLOAD_1 | opcode::DLOAD_1 250 | | opcode::ALOAD_1 => 251 | do_load!(1), 252 | opcode::ILOAD_2 | opcode::LLOAD_2 | opcode::FLOAD_2 | opcode::DLOAD_2 253 | | opcode::ALOAD_2 => 254 | do_load!(2), 255 | opcode::ILOAD_3 | opcode::LLOAD_3 | opcode::FLOAD_3 | opcode::DLOAD_3 256 | | opcode::ALOAD_3 => 257 | do_load!(3), 258 | opcode::IALOAD | opcode::LALOAD | opcode::FALOAD | opcode::DALOAD 259 | | opcode::AALOAD | opcode::BALOAD | opcode::CALOAD | opcode::SALOAD => { 260 | let Wrapping(index) = pop!(Value::Int); 261 | let array_rc = pop_not_null!(Value::ArrayReference); 262 | push!(array_rc.borrow_mut().get(index)); 263 | }, 264 | 265 | // same thing here 266 | opcode::ISTORE | opcode::LSTORE | opcode::FSTORE | opcode::DSTORE | opcode::ASTORE => 267 | with!(read_next_byte, do_store), 268 | opcode::ISTORE_0 | opcode::LSTORE_0 | opcode::FSTORE_0 | opcode::DSTORE_0 269 | | opcode::ASTORE_0 => 270 | do_store!(0), 271 | opcode::ISTORE_1 | opcode::LSTORE_1 | opcode::FSTORE_1 | opcode::DSTORE_1 272 | | opcode::ASTORE_1 => 273 | do_store!(1), 274 | opcode::ISTORE_2 | opcode::LSTORE_2 | opcode::FSTORE_2 | opcode::DSTORE_2 275 | | opcode::ASTORE_2 => 276 | do_store!(2), 277 | opcode::ISTORE_3 | opcode::LSTORE_3 | opcode::FSTORE_3 | opcode::DSTORE_3 278 | | opcode::ASTORE_3 => 279 | do_store!(3), 280 | opcode::IASTORE | opcode::LASTORE | opcode::FASTORE | opcode::DASTORE | opcode::AASTORE | opcode::BASTORE | opcode::CASTORE | opcode::SASTORE => { 281 | let value = pop!(); 282 | let Wrapping(index) = pop!(Value::Int); 283 | let array_rc = pop_not_null!(Value::ArrayReference); 284 | array_rc.borrow_mut().put(index, value); 285 | }, 286 | 287 | opcode::POP => { 288 | pop!(); 289 | }, 290 | opcode::POP2 => { 291 | match pop!() { 292 | Value::Long(_) | Value::Double(_) => (), 293 | _ => { 294 | pop!(); 295 | } 296 | } 297 | }, 298 | opcode::DUP => { 299 | let value = self.operand_stack.last().unwrap().clone(); 300 | push!(value); 301 | }, 302 | opcode::DUP_X1 => { 303 | let value1 = pop!(); 304 | let value2 = pop!(); 305 | 306 | push!(value1.clone(), value2, value1); 307 | }, 308 | opcode::DUP_X2 => { 309 | let value1 = pop!(); 310 | let value2 = pop!(); 311 | match value2 { 312 | Value::Long(_) | Value::Double(_) => { 313 | push!(value1.clone(), value2, value1); 314 | }, 315 | _ => { 316 | let value3 = pop!(); 317 | push!(value1.clone(), value3, value2, value1); 318 | }, 319 | } 320 | }, 321 | opcode::DUP2 => { 322 | let value1 = pop!(); 323 | match value1 { 324 | Value::Long(_) | Value::Double(_) => { 325 | push!(value1.clone(), value1); 326 | }, 327 | _ => { 328 | let value2 = pop!(); 329 | push!(value2.clone(), value1.clone(), value2, value1); 330 | }, 331 | } 332 | }, 333 | opcode::DUP2_X1 => unimplemented!(), 334 | opcode::DUP2_X2 => unimplemented!(), 335 | 336 | opcode::SWAP => { 337 | // both values need to be category 1 338 | let v1 = pop!(); 339 | let v2 = pop!(); 340 | push!(v1); 341 | push!(v2); 342 | }, 343 | 344 | opcode::IADD => do_binop!(Value::Int, Wrapping::::add), 345 | opcode::LADD => do_binop!(Value::Long, Wrapping::::add), 346 | opcode::FADD => do_binop!(Value::Float, f32::add), 347 | opcode::DADD => do_binop!(Value::Double, f64::add), 348 | opcode::ISUB => do_binop!(Value::Int, Wrapping::::sub), 349 | opcode::LSUB => do_binop!(Value::Long, Wrapping::::sub), 350 | opcode::FSUB => do_binop!(Value::Float, f32::sub), 351 | opcode::DSUB => do_binop!(Value::Double, f64::sub), 352 | opcode::IMUL => do_binop!(Value::Int, Wrapping::::mul), 353 | opcode::LMUL => do_binop!(Value::Long, Wrapping::::mul), 354 | opcode::FMUL => do_binop!(Value::Float, f32::mul), 355 | opcode::DMUL => do_binop!(Value::Double, f64::mul), 356 | opcode::IDIV => do_binop!(Value::Int, Wrapping::::div), 357 | opcode::LDIV => do_binop!(Value::Long, Wrapping::::div), 358 | opcode::FDIV => do_binop!(Value::Float, f32::div), 359 | opcode::DDIV => do_binop!(Value::Double, f64::div), 360 | opcode::IREM => do_binop!(Value::Int, Wrapping::::rem), 361 | opcode::LREM => do_binop!(Value::Long, Wrapping::::rem), 362 | opcode::FREM => do_binop!(Value::Float, f32::rem), 363 | opcode::DREM => do_binop!(Value::Double, f64::rem), 364 | // Issue #33037: Neg is missing for Wrapping 365 | opcode::INEG => push!(Value::Int(!pop!(Value::Int) + Wrapping(1))), 366 | opcode::LNEG => push!(Value::Long(!pop!(Value::Long) + Wrapping(1))), 367 | opcode::FNEG => push!(Value::Float(-pop!(Value::Float))), 368 | opcode::DNEG => push!(Value::Double(-pop!(Value::Double))), 369 | opcode::ISHL => { 370 | let Wrapping(s) = pop!(Value::Int); 371 | let v = pop!(Value::Int); 372 | push!(Value::Int(v << (s & 0x1F) as usize)); 373 | } 374 | opcode::LSHL => { 375 | let Wrapping(s) = pop!(Value::Int); 376 | let v = pop!(Value::Long); 377 | push!(Value::Long(v << (s & 0x3F) as usize)); 378 | } 379 | opcode::ISHR => { 380 | let Wrapping(s) = pop!(Value::Int); 381 | let v = pop!(Value::Int); 382 | push!(Value::Int(v >> (s & 0x1F) as usize)); 383 | } 384 | opcode::LSHR => { 385 | let Wrapping(s) = pop!(Value::Int); 386 | let v = pop!(Value::Long); 387 | push!(Value::Long(v >> (s & 0x3F) as usize)); 388 | } 389 | opcode::IUSHR => { 390 | let s = (pop!(Value::Int).0 & 0x1F) as usize; 391 | let v = pop!(Value::Int).0 as u32; 392 | push!(Value::Int(Wrapping((v >> s) as i32))) 393 | }, 394 | opcode::LUSHR => { 395 | let s = (pop!(Value::Int).0 & 0x3F) as usize; 396 | let v = pop!(Value::Long).0 as u64; 397 | push!(Value::Long(Wrapping((v >> s) as i64))) 398 | }, 399 | opcode::IAND => do_binop!(Value::Int, Wrapping::::bitand), 400 | opcode::LAND => do_binop!(Value::Long, Wrapping::::bitand), 401 | opcode::IOR => do_binop!(Value::Int, Wrapping::::bitor), 402 | opcode::LOR => do_binop!(Value::Long, Wrapping::::bitor), 403 | opcode::IXOR => do_binop!(Value::Int, Wrapping::::bitxor), 404 | opcode::LXOR => do_binop!(Value::Long, Wrapping::::bitxor), 405 | opcode::IINC => { 406 | let index = self.read_next_byte(); 407 | let c = self.read_next_byte() as i8 as i32; 408 | match self.local_variables[index as usize] { 409 | Some(Value::Int(ref mut v)) => *v += Wrapping(c), 410 | Some(ref v) => panic!("IINC: Expected an int, but was {:?}", v), 411 | None => panic!("IINC: Not a local variable at index {}", index), 412 | } 413 | }, 414 | 415 | opcode::I2L => map_top!(Value::Int(Wrapping(n)), Value::Long(Wrapping(n as i64))), 416 | opcode::I2F => map_top!(Value::Int(Wrapping(n)), Value::Float(n as f32)), 417 | opcode::I2D => map_top!(Value::Int(Wrapping(n)), Value::Double(n as f64)), 418 | opcode::L2I => map_top!(Value::Long(Wrapping(n)), Value::Int(Wrapping(n as i32))), 419 | opcode::L2F => map_top!(Value::Long(Wrapping(n)), Value::Float(n as f32)), 420 | opcode::L2D => map_top!(Value::Long(Wrapping(n)), Value::Double(n as f64)), 421 | opcode::F2I => map_top!(Value::Float(n), Value::Int(Wrapping(n as i32))), 422 | opcode::F2L => map_top!(Value::Float(n), Value::Long(Wrapping(n as i64))), 423 | opcode::F2D => map_top!(Value::Float(n), Value::Double(n as f64)), 424 | opcode::D2I => map_top!(Value::Double(n), Value::Int(Wrapping(n as i32))), 425 | opcode::D2L => map_top!(Value::Double(n), Value::Long(Wrapping(n as i64))), 426 | opcode::D2F => map_top!(Value::Double(n), Value::Float(n as f32)), 427 | opcode::I2B => map_top!(Value::Int(Wrapping(n)), Value::Int(Wrapping(n as i8 as i32))), 428 | opcode::I2C => map_top!(Value::Int(Wrapping(n)), Value::Int(Wrapping(n as u16 as i32))), 429 | opcode::I2S => map_top!(Value::Int(Wrapping(n)), Value::Int(Wrapping(n as i16 as i32))), 430 | 431 | opcode::IFEQ => do_if_int!(Wrapping::::eq), 432 | opcode::IFNE => do_if_int!(Wrapping::::ne), 433 | opcode::IFLT => do_if_int!(Wrapping::::lt), 434 | opcode::IFGE => do_if_int!(Wrapping::::ge), 435 | opcode::IFGT => do_if_int!(Wrapping::::gt), 436 | opcode::IFLE => do_if_int!(Wrapping::::le), 437 | 438 | opcode::IF_ICMPEQ => do_if_icmp!(Wrapping::::eq), 439 | opcode::IF_ICMPNE => do_if_icmp!(Wrapping::::ne), 440 | opcode::IF_ICMPLT => do_if_icmp!(Wrapping::::lt), 441 | opcode::IF_ICMPGT => do_if_icmp!(Wrapping::::gt), 442 | opcode::IF_ICMPGE => do_if_icmp!(Wrapping::::ge), 443 | opcode::IF_ICMPLE => do_if_icmp!(Wrapping::::le), 444 | 445 | opcode::IF_ACMPEQ => { 446 | let branch_offset = self.read_next_short() as i16; 447 | let v2 = pop!(); 448 | let v1 = pop!(); 449 | match (v1, v2) { 450 | (Value::ArrayReference(x), Value::ArrayReference(y)) => { 451 | if x.as_ref() as *const RefCell<_> == y.as_ref() as *const RefCell<_> { 452 | // 3 byte long instruction; read* operations move the PC. 453 | let this_pc_start = self.pc - 3; 454 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16; 455 | } 456 | }, 457 | (Value::ScalarReference(x), Value::ScalarReference(y)) => { 458 | if x.as_ref() as *const RefCell<_> == y.as_ref() as *const RefCell<_> { 459 | // 3 byte long instruction; read* operations move the PC. 460 | let this_pc_start = self.pc - 3; 461 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16; 462 | } 463 | } 464 | _ => (), 465 | } 466 | }, 467 | opcode::IF_ACMPNE => { 468 | let branch_offset = self.read_next_short() as i16; 469 | let v2 = pop!(); 470 | let v1 = pop!(); 471 | match (v1, v2) { 472 | (Value::ArrayReference(x), Value::ArrayReference(y)) => { 473 | if x.as_ref() as *const RefCell<_> != y.as_ref() as *const RefCell<_> { 474 | // 3 byte long instruction; read* operations move the PC. 475 | let this_pc_start = self.pc - 3; 476 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16; 477 | } 478 | }, 479 | (Value::ScalarReference(x), Value::ScalarReference(y)) => { 480 | if x.as_ref() as *const RefCell<_> != y.as_ref() as *const RefCell<_> { 481 | // 3 byte long instruction; read* operations move the PC. 482 | let this_pc_start = self.pc - 3; 483 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16; 484 | } 485 | }, 486 | _ => (), 487 | } 488 | }, 489 | 490 | opcode::GOTO => { 491 | let branch_offset = self.read_next_short() as i16; 492 | let this_pc_start = self.pc - 3; 493 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16; 494 | }, 495 | 496 | opcode::JSR => unimplemented!(), 497 | opcode::RET => unimplemented!(), 498 | 499 | opcode::TABLESWITCH => unimplemented!(), 500 | opcode::LOOKUPSWITCH => unimplemented!(), 501 | 502 | opcode::IRETURN | opcode::LRETURN | opcode::FRETURN | opcode::DRETURN 503 | | opcode::ARETURN => return self.operand_stack.pop(), 504 | opcode::RETURN => return None, 505 | 506 | opcode::GETSTATIC => { 507 | let index = self.read_next_short(); 508 | if let Some(RuntimeConstantPoolEntry::FieldRef(ref symref)) = 509 | self.current_class.get_constant_pool()[index] { 510 | let resolved_class = class_loader.resolve_class(&symref.class).unwrap(); 511 | let value = resolved_class.resolve_and_get_field(symref, class_loader); 512 | push!(value) 513 | } else { 514 | panic!("getstatic refers to non-field in constant pool"); 515 | } 516 | }, 517 | 518 | opcode::PUTSTATIC => { 519 | let index = self.read_next_short(); 520 | if let Some(RuntimeConstantPoolEntry::FieldRef(ref symref)) = 521 | self.current_class.get_constant_pool()[index] { 522 | let resolved_class = class_loader.resolve_class(&symref.class).unwrap(); 523 | let new_value = pop!(); 524 | resolved_class.resolve_and_put_field(symref, new_value, class_loader); 525 | } else { 526 | panic!("putstatic refers to non-field in constant pool"); 527 | } 528 | }, 529 | 530 | opcode::GETFIELD => { 531 | let index = self.read_next_short(); 532 | if let Some(RuntimeConstantPoolEntry::FieldRef(ref symref)) = 533 | self.current_class.get_constant_pool()[index] { 534 | match pop!() { 535 | Value::ScalarReference(object_rc) => { 536 | let value = object_rc.borrow().get_field(&symref.sig).clone(); 537 | push!(value); 538 | }, 539 | Value::ArrayReference(_) => panic!("getfield called on array"), 540 | Value::NullReference => panic!("NullPointerException"), 541 | _ => panic!("getfield called on a primitive value"), 542 | } 543 | } else { 544 | panic!("getfield refers to non-field in constant pool"); 545 | } 546 | }, 547 | 548 | opcode::PUTFIELD => { 549 | let index = self.read_next_short(); 550 | let value = pop!(); 551 | if let Some(RuntimeConstantPoolEntry::FieldRef(ref symref)) = 552 | self.current_class.get_constant_pool()[index] { 553 | match pop!() { 554 | Value::ScalarReference(object_rc) => { 555 | object_rc.borrow_mut().put_field(symref.sig.clone(), value); 556 | }, 557 | Value::ArrayReference(_) => panic!("putfield called on array"), 558 | Value::NullReference => panic!("NullPointerException"), 559 | _ => panic!("putfield called on a primitive value"), 560 | } 561 | } else { 562 | panic!("putfield refers to non-field in constant pool"); 563 | } 564 | }, 565 | 566 | opcode::INVOKEVIRTUAL => { 567 | let index = self.read_next_short(); 568 | if let Some(RuntimeConstantPoolEntry::MethodRef(ref symref)) = 569 | self.current_class.get_constant_pool()[index] { 570 | // TODO: this should throw Java exceptions instead of unwrapping 571 | let resolved_class = class_loader.resolve_class(&symref.class).unwrap(); 572 | let resolved_method = resolved_class.resolve_method(symref); 573 | // TODO: check for and 574 | // TODO: check protected accesses 575 | let num_args = symref.sig.params.len(); 576 | let args = self.pop_multi(num_args + 1); 577 | let object_class = { 578 | let object_value = &args[0]; 579 | match *object_value { 580 | Value::ScalarReference(ref scalar_rc) => 581 | scalar_rc.borrow().get_class().clone(), 582 | Value::ArrayReference(ref array_rc) => 583 | array_rc.borrow().get_class().clone(), 584 | Value::NullReference => panic!("NullPointerException"), 585 | _ => panic!("invokevirtual on a primitive type"), 586 | } 587 | }; 588 | match object_class.dispatch_method(resolved_method) { 589 | None => panic!("AbstractMethodError"), 590 | Some((actual_class, actual_method)) => { 591 | let result = actual_method.invoke(actual_class, class_loader, args); 592 | match result { 593 | None => (), 594 | Some(value) => self.operand_stack.push(value), 595 | } 596 | }, 597 | } 598 | } else { 599 | panic!("invokevirtual refers to non-method in constant pool"); 600 | } 601 | }, 602 | 603 | opcode::INVOKESPECIAL => { 604 | let index = self.read_next_short(); 605 | if let Some(RuntimeConstantPoolEntry::MethodRef(ref symref)) = 606 | self.current_class.get_constant_pool()[index] { 607 | // TODO: this should throw Java exceptions instead of unwrapping 608 | let resolved_class = class_loader.resolve_class(&symref.class).unwrap(); 609 | let resolved_method = resolved_class.resolve_method(symref); 610 | // TODO: check protected accesses 611 | // TODO: lots of other checks here too 612 | let num_args = symref.sig.params.len(); 613 | let args = self.pop_multi(num_args + 1); 614 | 615 | // check the three conditions from the spec 616 | let actual_method = { 617 | if resolved_class.access_flags & class_access_flags::ACC_SUPER == 0 618 | || !self.current_class.is_descendant(resolved_class.as_ref()) 619 | || resolved_method.symref.sig.name == "" { 620 | resolved_method 621 | } else { 622 | self.current_class.superclass.as_ref().and_then(|superclass| { 623 | superclass.find_method(&symref.sig) 624 | }).expect("AbstractMethodError") 625 | } 626 | }; 627 | let actual_class = class_loader.resolve_class(&actual_method.symref.class).unwrap(); 628 | let result = actual_method.invoke(actual_class.as_ref(), class_loader, 629 | args); 630 | match result { 631 | None => (), 632 | Some(value) => self.operand_stack.push(value), 633 | } 634 | } else { 635 | panic!("invokespecial refers to non-method in constant pool"); 636 | } 637 | }, 638 | 639 | opcode::INVOKESTATIC => { 640 | let index = self.read_next_short(); 641 | if let Some(RuntimeConstantPoolEntry::MethodRef(ref symref)) = 642 | self.current_class.get_constant_pool()[index] { 643 | // TODO: this should throw Java exceptions instead of unwrapping 644 | let resolved_class = class_loader.resolve_class(&symref.class).unwrap(); 645 | let resolved_method = resolved_class.resolve_method(symref); 646 | // TODO: check protected accesses 647 | // TODO: lots of other checks here too 648 | let num_args = symref.sig.params.len(); 649 | let args = self.pop_multi(num_args); 650 | let result = resolved_method.invoke(resolved_class.as_ref(), class_loader, 651 | args); 652 | match result { 653 | None => (), 654 | Some(value) => self.operand_stack.push(value), 655 | } 656 | } else { 657 | panic!("invokestatic refers to non-method in constant pool"); 658 | } 659 | }, 660 | 661 | opcode::NEW => { 662 | let index = self.read_next_short(); 663 | if let Some(RuntimeConstantPoolEntry::ClassRef(ref symref)) = 664 | self.current_class.get_constant_pool()[index] { 665 | // TODO check for interfaces, abstract classes 666 | let resolved_class = class_loader.resolve_class(symref).unwrap(); 667 | resolved_class.initialize(class_loader); 668 | let object = Scalar::new(resolved_class); 669 | let object_rc = Rc::new(RefCell::new(object)); 670 | push!(Value::ScalarReference(object_rc)); 671 | } else { 672 | panic!("new refers to non-class in constant pool"); 673 | } 674 | }, 675 | 676 | opcode::NEWARRAY => { 677 | let type_tag = self.read_next_byte(); 678 | let component_ty = match type_tag { 679 | 4 => Type::Boolean, 680 | 5 => Type::Char, 681 | 6 => Type::Float, 682 | 7 => Type::Double, 683 | 8 => Type::Byte, 684 | 9 => Type::Short, 685 | 10 => Type::Int, 686 | 11 => Type::Long, 687 | _ => panic!("newarray: bad type tag"), 688 | }; 689 | let class_sig = sig::Class::Array(Box::new(component_ty)); 690 | let class_symref = symref::Class { sig: class_sig }; 691 | let class = class_loader.resolve_class(&class_symref).unwrap(); 692 | 693 | match pop!() { 694 | Value::Int(Wrapping(length)) => { 695 | let array = Array::new(class, length); 696 | let array_rc = Rc::new(RefCell::new(array)); 697 | push!(Value::ArrayReference(array_rc)); 698 | }, 699 | _ => panic!("newarray called with non-int length"), 700 | } 701 | }, 702 | 703 | opcode::ANEWARRAY => unimplemented!(), 704 | 705 | opcode::ARRAYLENGTH => { 706 | let array_rc = pop_not_null!(Value::ArrayReference); 707 | let len = array_rc.borrow().len(); 708 | push!(Value::Int(Wrapping(len))); 709 | }, 710 | 711 | opcode::ATHROW => unimplemented!(), 712 | opcode::CHECKCAST => unimplemented!(), 713 | opcode::INSTANCEOF => unimplemented!(), 714 | opcode::MONITORENTER => unimplemented!(), 715 | opcode::MONITOREXIT => unimplemented!(), 716 | opcode::WIDE => unimplemented!(), 717 | opcode::MULTIANEWARRAY => unimplemented!(), 718 | 719 | opcode::IFNULL => { 720 | let branch_offset = self.read_next_short() as i16; 721 | if let Value::NullReference = pop!() { 722 | // 3 byte long instruction; read* operations move the PC. 723 | let this_pc_start = self.pc - 3; 724 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16 725 | } 726 | }, 727 | opcode::IFNONNULL => { 728 | let branch_offset = self.read_next_short() as i16; 729 | if let Value::NullReference = pop!() { 730 | () 731 | } else { 732 | // 3 byte long instruction; read* operations move the PC. 733 | let this_pc_start = self.pc - 3; 734 | self.pc = (this_pc_start as i32 + branch_offset as i32) as u16 735 | } 736 | }, 737 | 738 | opcode::GOTO_W => unimplemented!(), 739 | opcode::JSR_W => unimplemented!(), 740 | 741 | // reserved opcodes 742 | opcode::BREAKPOINT => unimplemented!(), 743 | opcode::IMPDEP1 => unimplemented!(), 744 | opcode::IMPDEP2 => unimplemented!(), 745 | 746 | _ => { 747 | println!("{}", self.code[(self.pc as usize) - 1]); 748 | panic!("unknown opcode") 749 | }, 750 | } 751 | } 752 | } 753 | } 754 | -------------------------------------------------------------------------------- /src/vm/mod.rs: -------------------------------------------------------------------------------- 1 | //! The public interface for the Java virtual machine. 2 | 3 | mod bytecode; 4 | mod class; 5 | mod class_loader; 6 | mod constant_pool; 7 | mod frame; 8 | mod native; 9 | mod value; 10 | 11 | use self::class_loader::ClassLoader; 12 | 13 | /// A symbolic reference to an entity in the runtime constant pool (§5.1). Symbolic references 14 | /// must be resolved (§5.4.3) before their usage by the interpreter. 15 | pub mod symref { 16 | use vm::sig; 17 | 18 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 19 | /// A symbolic reference to a class constant pool entry. 20 | pub struct Class { 21 | /// The signature of the class to which the symbolic reference refers. 22 | pub sig: sig::Class, 23 | } 24 | 25 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 26 | /// A symbolic reference to a field constant pool entry. 27 | pub struct Field { 28 | /// A symbolic reference to the class containing this field. 29 | pub class: Class, 30 | /// The signature of the field to which the symbolic reference refers. 31 | pub sig: sig::Field, 32 | } 33 | 34 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 35 | /// A symbolic reference to a method constant pool entry. 36 | pub struct Method { 37 | /// A symbolic reference to the class containing this method. 38 | pub class: Class, 39 | /// The signature of the method to which the symbolic reference refers. 40 | pub sig: sig::Method, 41 | } 42 | } 43 | 44 | /// Signatures of runtime constant pool entities that serve to uniquely identify those entities. 45 | /// These are derived from structures in the binary representation of the constant pool (§5.1). 46 | pub mod sig { 47 | use std::num::Wrapping; 48 | use vm::value::Value; 49 | 50 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 51 | /// Java language type information. 52 | pub enum Type { 53 | Byte, 54 | Char, 55 | Double, 56 | Float, 57 | Int, 58 | Long, 59 | Short, 60 | Boolean, 61 | Reference(Class), 62 | } 63 | 64 | impl Type { 65 | pub fn new(type_str: &str) -> Self { 66 | let (ty, rest) = Self::new_partial(type_str).unwrap(); 67 | if rest.len() > 0 { 68 | panic!("extra content at end of type descriptor") 69 | } else { 70 | ty 71 | } 72 | } 73 | 74 | /// Compute a sequence of types from a JVM-internal string representation. 75 | pub fn new_multi(multi_type_str: &str) -> (Vec, &str) { 76 | let mut types = vec![]; 77 | let mut remainder = multi_type_str; 78 | { 79 | let mut result = Ok(&mut types); 80 | while let Ok(types) = result { 81 | result = Self::new_partial(remainder).map(|(ty, new_remainder)| { 82 | types.push(ty); 83 | remainder = new_remainder; 84 | types 85 | }); 86 | } 87 | } 88 | (types, remainder) 89 | } 90 | 91 | /// Compute a type from a JVM-internal string representation. 92 | fn new_partial(type_str: &str) -> Result<(Self, &str), ()> { 93 | let (specifier, rest) = type_str.split_at(1); 94 | match specifier { 95 | "B" => Ok((Type::Byte, rest)), 96 | "C" => Ok((Type::Char, rest)), 97 | "D" => Ok((Type::Double, rest)), 98 | "F" => Ok((Type::Float, rest)), 99 | "I" => Ok((Type::Int, rest)), 100 | "J" => Ok((Type::Long, rest)), 101 | "S" => Ok((Type::Short, rest)), 102 | "Z" => Ok((Type::Boolean, rest)), 103 | "L" => { 104 | let end_index = rest.find(';').unwrap(); 105 | let (name_slice, rest) = rest.split_at(end_index); 106 | let name = String::from(name_slice); 107 | let scalar_type = Type::Reference(Class::Scalar(name)); 108 | Ok((scalar_type, rest.split_at(1).1)) 109 | }, 110 | "[" => { 111 | let (component_type, rest) = try!(Self::new_partial(rest)); 112 | let array_type = Type::Reference(Class::Array(Box::new(component_type))); 113 | Ok((array_type, rest)) 114 | }, 115 | _ => Err(()) 116 | } 117 | } 118 | 119 | /// Get the default value for the type. Java's default values for integers is `0`, floats 120 | /// `0.0`, characters `'\0'`, booleans `false`, and references `null`. 121 | pub fn default_value(&self) -> Value { 122 | match *self { 123 | Type::Byte | Type::Char | Type::Int | Type::Short | Type::Boolean => 124 | Value::Int(Wrapping(0)), 125 | Type::Double => Value::Double(0.0), 126 | Type::Float => Value::Float(0.0), 127 | Type::Long => Value::Long(Wrapping(0)), 128 | Type::Reference(_) => Value::NullReference, 129 | } 130 | } 131 | } 132 | 133 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 134 | /// A class signature. 135 | pub enum Class { 136 | /// The signature of a non-array class, parametrized by its name. 137 | Scalar(String), 138 | /// The signature of an array class, parametrized by the type of elements in the array. 139 | Array(Box), 140 | } 141 | 142 | impl Class { 143 | pub fn new(name: &str) -> Self { 144 | if name.starts_with('[') { 145 | let (_, component_type_str) = name.split_at(1); 146 | let component_type = Type::new(component_type_str); 147 | Class::Array(Box::new(component_type)) 148 | } else { 149 | Class::Scalar(String::from(name)) 150 | } 151 | } 152 | 153 | pub fn get_package(&self) -> Option { 154 | match *self { 155 | Class::Scalar(ref name) => { 156 | match name.rfind('/') { 157 | None => Some(String::from("")), 158 | Some(index) => Some(String::from(name.split_at(index).0)), 159 | } 160 | }, 161 | Class::Array(_) => None, 162 | } 163 | } 164 | } 165 | 166 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 167 | /// A field signature. 168 | pub struct Field { 169 | /// The name of the field. 170 | pub name: String, 171 | /// The type of the field. 172 | pub ty: Type, 173 | } 174 | 175 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 176 | /// A method signature. 177 | pub struct Method { 178 | /// The name of the method. 179 | pub name: String, 180 | /// The types of the parameters of the method. 181 | pub params: Vec, 182 | /// The return type of the method, or `None` if the method is `void`. 183 | pub return_ty: Option, 184 | } 185 | 186 | impl Method { 187 | pub fn new(name: &str, descriptor: &str) -> Self { 188 | if !descriptor.starts_with('(') { 189 | panic!("invalid method descriptor"); 190 | } 191 | let params_str = descriptor.split_at(1).1; 192 | let (params, remainder) = Type::new_multi(params_str); 193 | let (close_paren, return_ty_str) = remainder.split_at(1); 194 | if close_paren != ")" { 195 | panic!("invalid method descriptor"); 196 | } 197 | let return_ty = 198 | if return_ty_str == "V" { 199 | None 200 | } else { 201 | Some(Type::new(return_ty_str)) 202 | }; 203 | Method { 204 | name: String::from(name), 205 | params: params, 206 | return_ty: return_ty 207 | } 208 | } 209 | } 210 | } 211 | 212 | #[derive(Debug)] 213 | /// The top-level virtual machine. The virtual machine contains a reference to its _bootstrap class 214 | /// loader_, which is used to load the main class and all of that class's dependencies. 215 | /// 216 | /// The virtual machine's `start` method causes initialization of the boostrap class loader, which 217 | /// loads the main class and invokes the `main(String[])` method to begin execution of the program. 218 | /// The virtual machine terminates either when the program completes successfully, or when there is 219 | /// an uncaught error in the program or the internal implementation of the virtual machine itself. 220 | pub struct VirtualMachine { 221 | /// The bootstrap class loader used to initialize the virtual machine. 222 | bootstrap_class_loader: ClassLoader, 223 | } 224 | 225 | impl VirtualMachine { 226 | pub fn new() -> Self { 227 | VirtualMachine { 228 | bootstrap_class_loader: ClassLoader::new(), 229 | } 230 | } 231 | 232 | /// Begin execution of the virtual machine instance's `main(String[])` method. 233 | pub fn start(mut self, main_class: symref::Class) { 234 | let class = self.bootstrap_class_loader.load_class(&main_class.sig).unwrap(); 235 | class.initialize(&mut self.bootstrap_class_loader); 236 | let string_ty = sig::Type::Reference(sig::Class::Scalar(String::from("java/lang/String"))); 237 | let string_array_ty = sig::Type::Reference(sig::Class::Array(Box::new(string_ty))); 238 | let main_sig = sig::Method { 239 | name: String::from("main"), 240 | params: vec![string_array_ty], 241 | return_ty: None, 242 | }; 243 | let main_symref = symref::Method { 244 | class: main_class, 245 | sig: main_sig, 246 | }; 247 | let method = class.resolve_method(&main_symref); 248 | method.invoke(&class, &mut self.bootstrap_class_loader, vec![]); 249 | } 250 | } 251 | 252 | -------------------------------------------------------------------------------- /src/vm/native.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | use std::io::Write; 4 | use std::num::Wrapping; 5 | 6 | use vm::{sig, symref}; 7 | use vm::value::Value; 8 | 9 | pub struct NativeMethod(&'static Fn(Vec) -> Option); 10 | 11 | impl fmt::Debug for NativeMethod { 12 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 13 | write!(f, "") 14 | } 15 | } 16 | 17 | impl NativeMethod { 18 | pub fn invoke(&self, args: Vec) -> Option { 19 | self.0(args) 20 | } 21 | } 22 | 23 | const ARRAYCOPY: &'static Fn(Vec) -> Option = &(|args| { 24 | if let Value::ArrayReference(ref src_rc) = args[0] { 25 | if let Value::Int(Wrapping(src_offset)) = args[1] { 26 | if let Value::ArrayReference(ref dest_rc) = args[2] { 27 | if let Value::Int(Wrapping(dest_offset)) = args[3] { 28 | if let Value::Int(Wrapping(len)) = args[4] { 29 | let src = src_rc.borrow(); 30 | let mut dest = dest_rc.borrow_mut(); 31 | for i in 0..len { 32 | let value = src.get(src_offset + i); 33 | dest.put(dest_offset + i, value); 34 | } 35 | } else { 36 | panic!("length must be an int"); 37 | } 38 | } else { 39 | panic!("dest_offset must be an int"); 40 | } 41 | } else { 42 | panic!("ArrayStoreException"); 43 | } 44 | } else { 45 | panic!("src_offset must be an int"); 46 | } 47 | } else { 48 | panic!("ArrayStoreException"); 49 | } 50 | None 51 | }); 52 | 53 | const WRITE: &'static Fn(Vec) -> Option = &(|args| { 54 | if let Value::ArrayReference(ref b_rc) = args[1] { 55 | if let Value::Int(Wrapping(off)) = args[2] { 56 | if let Value::Int(Wrapping(len)) = args[3] { 57 | let b = b_rc.borrow(); 58 | let mut bytes = vec![]; 59 | for i in 0..len { 60 | // TODO error condition is probably not right here 61 | let value = b.get(off + i); 62 | if let Value::Int(Wrapping(byte)) = value { 63 | bytes.push(byte as u8); 64 | } else { 65 | panic!("bad value"); 66 | } 67 | } 68 | io::stdout().write_all(&bytes).expect("IOException"); 69 | None 70 | } else { 71 | panic!("len must be an int") 72 | } 73 | } else { 74 | panic!("off must be an int") 75 | } 76 | } else { 77 | panic!("b must be an array") 78 | } 79 | }); 80 | 81 | pub fn bind(symref: &symref::Method) -> Option { 82 | let system_symref = symref::Class { 83 | sig: sig::Class::Scalar(String::from("java/lang/System")), 84 | }; 85 | let object_ty = sig::Type::Reference(sig::Class::Scalar(String::from("java/lang/Object"))); 86 | let arraycopy_sig = sig::Method { 87 | name: String::from("arraycopy"), 88 | params: vec![object_ty.clone(), sig::Type::Int, object_ty.clone(), sig::Type::Int, 89 | sig::Type::Int], 90 | return_ty: None, 91 | }; 92 | let arraycopy_symref = symref::Method { 93 | class: system_symref.clone(), 94 | sig: arraycopy_sig, 95 | }; 96 | 97 | let stdout_symref = symref::Class { 98 | sig: sig::Class::Scalar(String::from("moon/RustStdout")), 99 | }; 100 | let byte_array_ty = sig::Type::Reference(sig::Class::Array(Box::new(sig::Type::Byte))); 101 | let write_sig = sig::Method { 102 | name: String::from("write"), 103 | params: vec![byte_array_ty, sig::Type::Int, sig::Type::Int], 104 | return_ty: None, 105 | }; 106 | let write_symref = symref::Method { 107 | class: stdout_symref.clone(), 108 | sig: write_sig, 109 | }; 110 | 111 | if *symref == arraycopy_symref { 112 | Some(NativeMethod(ARRAYCOPY)) 113 | } else if *symref == write_symref { 114 | Some(NativeMethod(WRITE)) 115 | } else { 116 | None 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/vm/value.rs: -------------------------------------------------------------------------------- 1 | //! Internal JVM representations of Java values. 2 | 3 | use std::cell::RefCell; 4 | use std::collections::HashMap; 5 | use std::num::Wrapping; 6 | use std::rc::Rc; 7 | 8 | use vm::class::Class; 9 | use vm::sig; 10 | 11 | /// A value in the Java virtual machine. 12 | #[derive(Debug, Clone)] 13 | pub enum Value { 14 | /// A 32-bit signed integral type, representing the Java types `byte`, `char`, `short`, `int`, 15 | /// and `boolean`. 16 | Int(Wrapping), 17 | /// A 32-bit floating-point type, representing the Java type `float`. 18 | Float(f32), 19 | /// A 64-bit signed integral type, representing the Java type `long`. 20 | Long(Wrapping), 21 | /// A 64-bit floating-point type, representing the Java type `double`. 22 | Double(f64), 23 | /// A reference to a scalar Java object in the heap. 24 | ScalarReference(Rc>), 25 | /// A reference to a Java array in the heap. 26 | ArrayReference(Rc>), 27 | /// A reference to a Java object which is `null`. 28 | NullReference, 29 | } 30 | 31 | #[derive(Debug)] 32 | /// An instance of a non-array object. 33 | pub struct Scalar { 34 | /// A reference to the object's creating class. 35 | class: Rc, 36 | /// The instance (non-`static`) fields of the object. 37 | fields: HashMap, 38 | } 39 | 40 | // TODO the semantics of getting and putting fields are incorrect w/r/t inheritance 41 | impl Scalar { 42 | pub fn new(class: Rc) -> Self { 43 | match class.symref.sig { 44 | sig::Class::Scalar(_) => { 45 | let field_sigs = class.collect_instance_fields(); 46 | let mut fields = HashMap::new(); 47 | for sig in field_sigs { 48 | let value = sig.ty.default_value(); 49 | fields.insert(sig, value); 50 | } 51 | Scalar { 52 | class: class, 53 | fields: fields, 54 | } 55 | }, 56 | sig::Class::Array(_) => panic!("can't construct scalar from array class"), 57 | } 58 | } 59 | 60 | pub fn get_class(&self) -> Rc { 61 | self.class.clone() 62 | } 63 | 64 | pub fn get_field(&self, sig: &sig::Field) -> Value { 65 | self.fields.get(sig).unwrap().clone() 66 | } 67 | 68 | pub fn put_field(&mut self, sig: sig::Field, value: Value) { 69 | self.fields.insert(sig, value); 70 | } 71 | } 72 | 73 | #[derive(Debug)] 74 | /// An instance of an array object. 75 | pub struct Array { 76 | /// A reference to the (synthetic) array class. 77 | class: Rc, 78 | /// The array data. 79 | array: Vec, 80 | } 81 | 82 | impl Array { 83 | pub fn new(class: Rc, length: i32) -> Self { 84 | if length < 0 { 85 | panic!("NegativeArraySizeException"); 86 | } 87 | match class.symref.sig { 88 | sig::Class::Scalar(_) => panic!("can't construct array from scalar class"), 89 | sig::Class::Array(ref component_ty) => { 90 | let mut array = Vec::with_capacity(length as usize); 91 | for _ in 0..length { 92 | array.push(component_ty.default_value()); 93 | } 94 | Array { 95 | class: class.clone(), 96 | array: array, 97 | } 98 | }, 99 | } 100 | } 101 | 102 | pub fn get_class(&self) -> Rc { 103 | self.class.clone() 104 | } 105 | 106 | pub fn get(&self, index: i32) -> Value { 107 | if index < 0 || (index as usize) >= self.array.len() { 108 | panic!("ArrayIndexOutOfBoundsException") 109 | } 110 | self.array[index as usize].clone() 111 | } 112 | 113 | pub fn put(&mut self, index: i32, value: Value) { 114 | if index < 0 || (index as usize) >= self.array.len() { 115 | panic!("ArrayIndexOutOfBoundsException"); 116 | } 117 | self.array[index as usize] = value; 118 | } 119 | 120 | pub fn len(&self) -> i32 { 121 | self.array.len() as i32 122 | } 123 | } 124 | 125 | -------------------------------------------------------------------------------- /tests/hello_world.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | extern crate rust_jvm; 4 | extern crate nom; 5 | 6 | use self::rust_jvm::vm::{sig, symref, VirtualMachine}; 7 | 8 | #[test] 9 | fn test_hello_world() { 10 | let class_sig = sig::Class::new("HelloWorld"); 11 | let class_symref = symref::Class { sig: class_sig }; 12 | let vm = VirtualMachine::new(); 13 | vm.start(class_symref); 14 | } 15 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | mod hello_world; 2 | --------------------------------------------------------------------------------