├── .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 [](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 extends Annotation> 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 |
--------------------------------------------------------------------------------