Skip to content

Commit

Permalink
Add support for basic system native calls (#35)
Browse files Browse the repository at this point in the history
Add support for basic system native calls
  • Loading branch information
hextriclosan authored Oct 5, 2024
1 parent e91c1ab commit 46b9204
Show file tree
Hide file tree
Showing 17 changed files with 146 additions and 13 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ctor::ctor;
use std::env;
use std::sync::Once;
use std::time::{SystemTime, UNIX_EPOCH};
use vm::vm::VM;

static INIT: Once = Once::new();
Expand Down Expand Up @@ -254,6 +255,23 @@ fn should_do_composite_pattern() {
assert_eq!(700, get_int(last_frame_value))
}

#[test]
fn should_do_native_call_on_system() {
let start = SystemTime::now();
let since_the_epoch = start
.duration_since(UNIX_EPOCH)
.expect("Time went backwards");
let expected_millis = since_the_epoch.as_millis() as i64;

let mut vm = VM::new("std");
let last_frame_value = vm
.run("samples.nativecall.system.NativeCallSystem")
.unwrap();

let actual_millis = get_long(last_frame_value);
assert!((expected_millis..expected_millis + 2000).contains(&actual_millis))
}

fn get_int(locals: Option<Vec<i32>>) -> i32 {
*locals.unwrap().last().unwrap()
}
Expand Down
7 changes: 7 additions & 0 deletions tests/test_data/NativeCallSystem.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package samples.nativecall.system;

public class NativeCallSystem {
public static void main(String[] args) {
long currentTimeMillis = System.currentTimeMillis();
}
}
Binary file not shown.
Binary file added tests/test_data/std/java/lang/System.class
Binary file not shown.
9 changes: 9 additions & 0 deletions tests/test_data/std/java/lang/System.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// javac --patch-module java.base=. System.java
package java.lang;

public final class System {
private System() {
}

public static native long currentTimeMillis();
}
1 change: 1 addition & 0 deletions vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ edition = "2021"
jclass = {path = "../jclass"}
jdescriptor = {path = "../jdescriptor"}
indexmap = "2.5.0"
once_cell = "1.20.1"
8 changes: 7 additions & 1 deletion vm/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::error::ErrorKind::{ClassFile, ConstantPool, Execution, Io};
use crate::error::ErrorKind::{ClassFile, ConstantPool, Execution, Io, Native};
use std::fmt::{Debug, Display, Formatter};
use std::{io, result};

Expand All @@ -20,6 +20,10 @@ impl Error {
Self::new(Execution(String::from(descr)))
}

pub(crate) fn new_native(descr: &String) -> Error {
Self::new(Native(String::from(descr)))
}

pub fn kind(&self) -> &ErrorKind {
&self.0
}
Expand All @@ -36,6 +40,7 @@ impl Display for Error {
ClassFile(err) => write!(f, "ClassFile Error: {err}"),
ConstantPool(descr) => write!(f, "ConstantPool Error: {descr}"),
Execution(descr) => write!(f, "Execution Error: {descr}"),
Native(descr) => write!(f, "Native Call Error: {descr}"),

ErrorKind::__Nonexhaustive => unreachable!(),
}
Expand All @@ -54,6 +59,7 @@ pub enum ErrorKind {
ClassFile(String),
ConstantPool(String),
Execution(String),
Native(String),

#[doc(hidden)]
__Nonexhaustive,
Expand Down
35 changes: 24 additions & 11 deletions vm/src/execution_engine/engine.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::error::Error;
use crate::execution_engine::opcode::*;
use crate::execution_engine::system_native_table::invoke_native_method;
use crate::heap::heap::Heap;
use crate::method_area::java_method::JavaMethod;
use crate::method_area::method_area::MethodArea;
Expand Down Expand Up @@ -487,7 +488,6 @@ impl Engine {
stack_frame.push(value2);
stack_frame.push(value1);


stack_frame.incr_pc();
println!("DUP_X1 -> value1={value1}, value2={value2}, value1={value1}");
}
Expand Down Expand Up @@ -594,7 +594,7 @@ impl Engine {
IUSHR => {
let b = stack_frame.pop() as u32;
let a = stack_frame.pop() as u32;

let b_trunc = b & 0b00011111u32;
let result = a >> b_trunc;
stack_frame.push(result as i32);
Expand Down Expand Up @@ -981,21 +981,33 @@ impl Engine {
// all static fields of the class should be initialized
// at this point

let mut next_frame = static_method.new_stack_frame()?;
let arg_num = static_method.get_method_descriptor().arguments_length();

let method_args: Vec<i32> = (0..arg_num)
.map(|_| stack_frame.pop())
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect();
method_args
.iter()
.enumerate()
.for_each(|(index, val)| next_frame.set_local(index, *val));

stack_frames.push(next_frame);
if static_method.is_native() {
let full_native_signature = format!("{class_name}:{full_signature}");
println!(
"<Calling native method> -> {full_native_signature} ({method_args:?})"
);

let result = invoke_native_method(&full_native_signature, &method_args)?;

result.iter().rev().for_each(|x| stack_frame.push(*x));
} else {
let mut next_frame = static_method.new_stack_frame()?;

method_args
.iter()
.enumerate()
.for_each(|(index, val)| next_frame.set_local(index, *val));

stack_frames.push(next_frame);
}
println!("INVOKESTATIC -> {class_name}.{method_name}({method_args:?})");
}
INVOKEINTERFACE => {
Expand Down Expand Up @@ -1121,11 +1133,12 @@ impl Engine {
let objectref = stack_frame.pop();
// todo: implementation here
stack_frame.push(objectref);

println!("CHECKCAST -> class_constpool_index={class_constpool_index}, objectref={objectref}");
todo!("add implementation");
}
IFNULL => { //todo: this one is opposite to IFNE ops code
IFNULL => {
//todo: this one is opposite to IFNE ops code
let value = stack_frame.pop();
let offset = Self::get_two_bytes_ahead(stack_frame);
stack_frame.advance_pc(if value == 0 { offset } else { 3 });
Expand Down
1 change: 1 addition & 0 deletions vm/src/execution_engine/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub(crate) mod engine;
pub(crate) mod opcode;
mod system_native_table;
28 changes: 28 additions & 0 deletions vm/src/execution_engine/system_native_table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::error::Error;
use crate::system_native::system::current_time_millis_wrp;
use once_cell::sync::Lazy;
use std::collections::HashMap;

static SYSTEM_NATIVE_TABLE: Lazy<HashMap<&'static str, fn(&[i32]) -> Vec<i32>>> =
Lazy::new(|| {
let mut table = HashMap::new();
table.insert(
"java/lang/System:currentTimeMillis:()J",
current_time_millis_wrp as fn(&[i32]) -> Vec<i32>,
);

table
});

pub(crate) fn invoke_native_method(
method_signature: &str,
args: &[i32],
) -> crate::error::Result<Vec<i32>> {
let native_method = SYSTEM_NATIVE_TABLE.get(method_signature).ok_or_else(|| {
Error::new_native(&format!("Native method {method_signature} not found"))
})?;

let result = native_method(args);

Ok(result)
}
1 change: 1 addition & 0 deletions vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod vm;
mod execution_engine;
mod heap;
mod method_area;
mod system_native;
7 changes: 7 additions & 0 deletions vm/src/method_area/java_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(crate) struct JavaMethod {
class_name: String,
name_signature: String,
code_context: Option<CodeContext>,
native: bool,
}

#[derive(Debug)]
Expand Down Expand Up @@ -45,12 +46,14 @@ impl JavaMethod {
class_name: &str,
name_signature: &str,
code_context: Option<CodeContext>,
native: bool,
) -> Self {
Self {
method_descriptor,
class_name: class_name.to_string(),
name_signature: name_signature.to_string(),
code_context,
native,
}
}

Expand All @@ -72,4 +75,8 @@ impl JavaMethod {
pub fn get_method_descriptor(&self) -> &MethodDescriptor {
&self.method_descriptor
}

pub fn is_native(&self) -> bool {
self.native
}
}
5 changes: 4 additions & 1 deletion vm/src/method_area/method_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ impl MethodArea {

let code_context = if !method_info
.access_flags()
.intersects(MethodFlags::ACC_ABSTRACT|MethodFlags::ACC_NATIVE)
.intersects(MethodFlags::ACC_ABSTRACT | MethodFlags::ACC_NATIVE)
{
AttributesHelper::new(method_info.attributes())
.get_code()
Expand All @@ -203,13 +203,16 @@ impl MethodArea {
))
})?;

let native = method_info.access_flags().contains(MethodFlags::ACC_NATIVE);

method_by_signature.insert(
key.clone(),
Rc::new(JavaMethod::new(
method_descriptor,
class_name,
&key,
code_context,
native,
)),
);
}
Expand Down
1 change: 1 addition & 0 deletions vm/src/system_native/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub(crate) mod system;
16 changes: 16 additions & 0 deletions vm/src/system_native/strict_math.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// use crate::stack::stack_frame::i32toi64;
//
// pub(crate) fn subtract_exact_wrp(args: &[i32]) -> Vec<i32> {
// let i = i32toi64(args[1], args[0]);
// let j = i32toi64(args[3], args[2]);
//
// let millis = subtract_exact(i, j);
//
// let low = millis as i32;
// let high = (millis >> 32) as i32;
//
// vec![high, low]
// }
// fn subtract_exact(first: i64, second: i64) -> i64 {
// first.checked_sub(second).expect("Subtraction overflow")
// }
15 changes: 15 additions & 0 deletions vm/src/system_native/system.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pub(crate) fn current_time_millis_wrp(_args: &[i32]) -> Vec<i32> {
let millis = current_time_millis();

let low = millis as i32;
let high = (millis >> 32) as i32;

vec![high, low]
}
fn current_time_millis() -> i64 {
let now = std::time::SystemTime::now();
let since_the_epoch = now
.duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards");
since_the_epoch.as_millis() as i64
}

0 comments on commit 46b9204

Please sign in to comment.