Skip to content

Commit 37753b7

Browse files
committed
Implement basic calendar functions
1 parent fdadcd7 commit 37753b7

File tree

4 files changed

+210
-16
lines changed

4 files changed

+210
-16
lines changed

Cargo.lock

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

java_runtime/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ parking_lot = { workspace = true }
1212
tracing = { workspace = true }
1313

1414
async-lock = { version = "^3.4", default-features = false }
15+
chrono = { version = "^0.4", default-features = false }
1516
encoding_rs = { version = "^0.8", features = ["alloc"], default-features = false }
1617
event-listener = { version = "^5.3", default-features = false }
1718
hashbrown = { version = "^0.14", features = ["ahash"], default-features = false }
Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloc::vec;
22

3-
use java_class_proto::JavaMethodProto;
3+
use java_class_proto::{JavaFieldProto, JavaMethodProto};
44
use java_constants::MethodAccessFlags;
55
use jvm::{ClassInstanceRef, Jvm, Result};
66

@@ -16,47 +16,80 @@ impl Calendar {
1616
parent_class: Some("java/lang/Object"),
1717
interfaces: vec![],
1818
methods: vec![
19+
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
1920
JavaMethodProto::new("getInstance", "()Ljava/util/Calendar;", Self::get_instance, MethodAccessFlags::STATIC),
2021
JavaMethodProto::new("setTime", "(Ljava/util/Date;)V", Self::set_time, Default::default()),
2122
JavaMethodProto::new("getTime", "()Ljava/util/Date;", Self::get_time, Default::default()),
2223
JavaMethodProto::new("set", "(II)V", Self::set, Default::default()),
2324
JavaMethodProto::new("get", "(I)I", Self::get, Default::default()),
25+
JavaMethodProto::new_abstract("computeTime", "()V", Default::default()),
26+
JavaMethodProto::new_abstract("computeFields", "()V", Default::default()),
27+
],
28+
fields: vec![
29+
JavaFieldProto::new("time", "J", Default::default()),
30+
JavaFieldProto::new("fields", "[I", Default::default()),
2431
],
25-
fields: vec![],
2632
}
2733
}
2834

2935
async fn get_instance(jvm: &Jvm, _: &mut RuntimeContext) -> Result<ClassInstanceRef<Calendar>> {
30-
tracing::warn!("stub java.util.Calendar::getInstance()");
36+
tracing::debug!("java.util.Calendar::getInstance()");
3137

3238
let instance = jvm.new_class("java/util/GregorianCalendar", "()V", []).await?;
3339

3440
Ok(instance.into())
3541
}
3642

37-
async fn set_time(_: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, date: ClassInstanceRef<Date>) -> Result<()> {
38-
tracing::warn!("stub java.util.Calendar::setTime({:?}, {:?})", &this, &date);
43+
async fn init(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>) -> Result<()> {
44+
tracing::debug!("java.util.Calendar::<init>({:?})", &this);
45+
46+
let _: () = jvm.invoke_special(&this, "java/lang/Object", "<init>", "()V", ()).await?;
47+
48+
// TODO constant
49+
let fields = jvm.instantiate_array("I", 17).await?;
50+
jvm.put_field(&mut this, "fields", "[I", fields).await?;
51+
52+
Ok(())
53+
}
54+
55+
async fn set_time(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>, date: ClassInstanceRef<Date>) -> Result<()> {
56+
tracing::debug!("java.util.Calendar::setTime({:?}, {:?})", &this, &date);
57+
58+
let time: i64 = jvm.invoke_virtual(&date, "getTime", "()J", ()).await?;
59+
jvm.put_field(&mut this, "time", "J", time).await?;
60+
61+
let _: () = jvm.invoke_virtual(&this, "computeFields", "()V", ()).await?;
3962

4063
Ok(())
4164
}
4265

4366
async fn get_time(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<ClassInstanceRef<Date>> {
44-
tracing::warn!("stub java.util.Calendar::getTime({:?})", &this);
67+
tracing::debug!("java.util.Calendar::getTime({:?})", &this);
4568

46-
let date = jvm.new_class("java/util/Date", "()V", ()).await?;
69+
let time: i64 = jvm.get_field(&this, "time", "J").await?;
70+
let date = jvm.new_class("java/util/Date", "(J)V", (time,)).await?;
4771

4872
Ok(date.into())
4973
}
5074

51-
async fn set(_: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, field: i32, value: i32) -> Result<()> {
52-
tracing::warn!("stub java.util.Calendar::set({:?}, {:?}, {:?})", &this, field, value);
75+
async fn set(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, field: i32, value: i32) -> Result<()> {
76+
tracing::debug!("java.util.Calendar::set({:?}, {:?}, {:?})", &this, field, value);
77+
78+
let mut fields = jvm.get_field(&this, "fields", "[I").await?;
79+
jvm.store_array(&mut fields, field as usize, vec![value]).await?;
80+
81+
let _: () = jvm.invoke_virtual(&this, "computeTime", "()V", ()).await?;
82+
let _: () = jvm.invoke_virtual(&this, "computeFields", "()V", ()).await?;
5383

5484
Ok(())
5585
}
5686

57-
async fn get(_: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, field: i32) -> Result<i32> {
58-
tracing::warn!("stub java.util.Calendar::get({:?}, {:?})", &this, field);
87+
async fn get(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>, field: i32) -> Result<i32> {
88+
tracing::debug!("java.util.Calendar::get({:?}, {:?})", &this, field);
89+
90+
let fields = jvm.get_field(&this, "fields", "[I").await?;
91+
let value = jvm.load_array(&fields, field as usize, 1).await?[0];
5992

60-
Ok(0)
93+
Ok(value)
6194
}
6295
}

java_runtime/src/classes/java/util/gregorian_calendar.rs

Lines changed: 145 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use alloc::vec;
1+
use alloc::{vec, vec::Vec};
2+
3+
use chrono::{DateTime, Datelike, FixedOffset, TimeZone, Timelike};
24

35
use java_class_proto::JavaMethodProto;
46
use jvm::{ClassInstanceRef, Jvm, Result};
@@ -14,13 +16,152 @@ impl GregorianCalendar {
1416
name: "java/util/GregorianCalendar",
1517
parent_class: Some("java/util/Calendar"),
1618
interfaces: vec![],
17-
methods: vec![JavaMethodProto::new("<init>", "()V", Self::init, Default::default())],
19+
methods: vec![
20+
JavaMethodProto::new("<init>", "()V", Self::init, Default::default()),
21+
JavaMethodProto::new("computeTime", "()V", Self::compute_time, Default::default()),
22+
JavaMethodProto::new("computeFields", "()V", Self::compute_fields, Default::default()),
23+
],
1824
fields: vec![],
1925
}
2026
}
2127

22-
async fn init(_: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
23-
tracing::warn!("stub java.util.GregorianCalendar::<init>({:?})", &this);
28+
async fn init(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
29+
tracing::debug!("java.util.GregorianCalendar::<init>({:?})", &this);
30+
31+
let _: () = jvm.invoke_special(&this, "java/util/Calendar", "<init>", "()V", ()).await?;
32+
33+
Ok(())
34+
}
35+
36+
async fn compute_time(jvm: &Jvm, _: &mut RuntimeContext, mut this: ClassInstanceRef<Self>) -> Result<()> {
37+
tracing::debug!("java.util.GregorianCalendar::computeTime({:?})", &this);
38+
39+
// fields -> time
40+
41+
let fields = jvm.get_field(&this, "fields", "[I").await?;
42+
43+
// TODO constant
44+
let fields: Vec<i32> = jvm.load_array(&fields, 0, 17).await?;
45+
let _era = fields[0];
46+
let year = fields[1];
47+
let month = fields[2];
48+
let _week_of_year = fields[3];
49+
let _week_of_month = fields[4];
50+
let date = fields[5];
51+
let _day_of_year = fields[6];
52+
let _day_of_week = fields[7];
53+
let _day_of_week_in_month = fields[8];
54+
let _am_pm = fields[9];
55+
let _hour = fields[10];
56+
let hour_of_day = fields[11];
57+
let minute = fields[12];
58+
let second = fields[13];
59+
let millisecond = fields[14];
60+
let zone_offset = fields[15]; // raw offset from GMT in milliseconds
61+
let _dst_offset = fields[16];
62+
63+
// TODO handle more complex cases
64+
let tz = FixedOffset::east_opt(zone_offset * 1000).unwrap();
65+
let timestamp = tz
66+
.with_ymd_and_hms(year, (month + 1) as _, date as _, hour_of_day as _, minute as _, second as _)
67+
.unwrap()
68+
.timestamp_millis() as i64;
69+
70+
let calculated_time = timestamp + millisecond as i64;
71+
72+
jvm.put_field(&mut this, "time", "J", calculated_time).await?;
73+
74+
Ok(())
75+
}
76+
77+
async fn compute_fields(jvm: &Jvm, _: &mut RuntimeContext, this: ClassInstanceRef<Self>) -> Result<()> {
78+
tracing::debug!("java.util.GregorianCalendar::computeFields({:?})", &this);
79+
80+
// time -> fields
81+
82+
let time: i64 = jvm.get_field(&this, "time", "J").await?;
83+
let date_time = DateTime::from_timestamp_millis(time as _).unwrap();
84+
85+
let calculated_fields = vec![
86+
1, // CE
87+
date_time.year(),
88+
date_time.month() as i32 - 1,
89+
date_time.iso_week().week() as _,
90+
(date_time.day() / 7) as _, // TODO correctly get
91+
date_time.day() as _,
92+
date_time.ordinal() as _,
93+
date_time.weekday().number_from_monday() as _,
94+
(date_time.day() % 7) as _, // TODO correctly get
95+
(date_time.hour() / 12) as _,
96+
(date_time.hour() % 12) as _,
97+
date_time.hour() as _,
98+
date_time.minute() as _,
99+
date_time.second() as _,
100+
(date_time.nanosecond() / 1_000_000) as _,
101+
0,
102+
0,
103+
];
104+
105+
let mut fields = jvm.get_field(&this, "fields", "[I").await?;
106+
jvm.store_array(&mut fields, 0, calculated_fields).await?;
107+
108+
Ok(())
109+
}
110+
}
111+
112+
#[cfg(test)]
113+
mod test {
114+
use jvm::Result;
115+
116+
use crate::test::test_jvm;
117+
118+
#[tokio::test]
119+
async fn test_gregorian_calendar() -> Result<()> {
120+
let jvm = test_jvm().await?;
121+
122+
let timestamp = 0i64;
123+
let calendar = jvm
124+
.invoke_static("java/util/Calendar", "getInstance", "()Ljava/util/Calendar;", ())
125+
.await?;
126+
let date = jvm.new_class("java/util/Date", "(J)V", (timestamp,)).await?;
127+
128+
let _: () = jvm.invoke_virtual(&calendar, "setTime", "(Ljava/util/Date;)V", (date,)).await?;
129+
let year: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (1,)).await?;
130+
assert_eq!(1970, year);
131+
132+
let month: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (2,)).await?;
133+
assert_eq!(0, month);
134+
135+
let day: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (5,)).await?;
136+
assert_eq!(1, day);
137+
138+
let timestamp = 737521516000i64;
139+
let date = jvm.new_class("java/util/Date", "(J)V", (timestamp,)).await?;
140+
141+
let _: () = jvm.invoke_virtual(&calendar, "setTime", "(Ljava/util/Date;)V", (date,)).await?;
142+
143+
let year: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (1,)).await?;
144+
assert_eq!(1993, year);
145+
146+
let month: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (2,)).await?;
147+
assert_eq!(4, month);
148+
149+
let day: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (5,)).await?;
150+
assert_eq!(16, day);
151+
152+
let hour: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (11,)).await?;
153+
assert_eq!(3, hour);
154+
155+
let minute: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (12,)).await?;
156+
assert_eq!(5, minute);
157+
158+
let second: i32 = jvm.invoke_virtual(&calendar, "get", "(I)I", (13,)).await?;
159+
assert_eq!(16, second);
160+
161+
let _: () = jvm.invoke_virtual(&calendar, "set", "(II)V", (1, 1999)).await?;
162+
let date = jvm.invoke_virtual(&calendar, "getTime", "()Ljava/util/Date;", ()).await?;
163+
let timestamp: i64 = jvm.invoke_virtual(&date, "getTime", "()J", ()).await?;
164+
assert_eq!(926823916000, timestamp);
24165

25166
Ok(())
26167
}

0 commit comments

Comments
 (0)