Skip to content

Commit f2d7c5b

Browse files
committed
Implement VM bits necessary for interpolated include URLs and try blocks.
1 parent f2bfb31 commit f2d7c5b

File tree

3 files changed

+111
-6
lines changed

3 files changed

+111
-6
lines changed

esi/src/compiler_types.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl Immediate {
7474
buf.put_u32_le(*req_id);
7575
}
7676
Immediate::ReqIdList(reqid_list) => {
77-
buf.put_u32_le(reqid_list.len().try_into().unwrap());
77+
buf.put_u16_le(reqid_list.len().try_into().unwrap());
7878
for req_id in reqid_list {
7979
buf.put_u32_le(*req_id);
8080
}

esi/src/vm.rs

+42-3
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,28 @@ fn run<T: EnvironmentApi>(ctx: ProgramContext, _env: Environment, api: T) -> Res
9191
let req_handle = api.request(url.to_bytes());
9292
state.requests[reqid] = req_handle;
9393
}
94-
OP_SUCCESS => panic!("unknown opcode: {}", opcode),
94+
OP_SUCCESS => {
95+
if state.ip + 2 > ctx.code_length {
96+
break;
97+
}
98+
let reqid_count = unsafe { read_u16(ctx.code_ptr.add(state.ip)) } as usize;
99+
state.ip += 2;
100+
101+
let mut got_failure = false;
102+
for _ in 0..reqid_count {
103+
let reqid = unsafe { read_u32(ctx.code_ptr.add(state.ip)) } as usize;
104+
state.ip += 4;
105+
106+
if !got_failure {
107+
let req_handle = state.requests[reqid];
108+
if api.get_response(req_handle) == Response::Failure {
109+
got_failure = true;
110+
}
111+
}
112+
}
113+
114+
state.stack.push(Value::Bool(!got_failure));
115+
}
95116
OP_JUMP => {
96117
if state.ip + 4 > ctx.code_length {
97118
break;
@@ -139,7 +160,11 @@ fn run<T: EnvironmentApi>(ctx: ProgramContext, _env: Environment, api: T) -> Res
139160
let right = state.stack.pop().unwrap();
140161
state.stack.push(Value::Bool(left == right));
141162
}
142-
OP_NOTEQUALS => panic!("unknown opcode: {}", opcode),
163+
OP_NOTEQUALS => {
164+
let left = state.stack.pop().unwrap();
165+
let right = state.stack.pop().unwrap();
166+
state.stack.push(Value::Bool(left != right));
167+
}
143168
OP_LESSTHAN => panic!("unknown opcode: {}", opcode),
144169
OP_LESSTHANOREQUALS => panic!("unknown opcode: {}", opcode),
145170
OP_GREATERTHAN => panic!("unknown opcode: {}", opcode),
@@ -151,7 +176,12 @@ fn run<T: EnvironmentApi>(ctx: ProgramContext, _env: Environment, api: T) -> Res
151176
OP_HASINSENSITIVE => panic!("unknown opcode: {}", opcode),
152177
OP_MATCHES => panic!("unknown opcode: {}", opcode),
153178
OP_MATCHESINSENSITIVE => panic!("unknown opcode: {}", opcode),
154-
OP_ADD => panic!("unknown opcode: {}", opcode),
179+
OP_ADD => {
180+
let right = state.stack.pop().unwrap();
181+
let left = state.stack.pop().unwrap();
182+
let value = left.add(right);
183+
state.stack.push(value);
184+
}
155185
OP_SUBTRACT => panic!("unknown opcode: {}", opcode),
156186
OP_MULTIPLY => panic!("unknown opcode: {}", opcode),
157187
OP_DIVIDE => panic!("unknown opcode: {}", opcode),
@@ -190,6 +220,7 @@ mod tests {
190220
struct TestApi {}
191221
impl<'a> EnvironmentApi for &'a TestApi {
192222
fn request(&self, url: &[u8]) -> RequestHandle {
223+
println!("request: {:?}", str::from_utf8(url).unwrap());
193224
1
194225
}
195226

@@ -225,7 +256,15 @@ not me
225256
found me!
226257
</esi:otherwise>
227258
</esi:choose>
259+
<esi:include src="/a$(foo)b">
260+
<esi:try>
261+
<esi:attempt>
228262
<esi:include src="/a">
263+
</esi:attempt>
264+
<esi:except>
265+
except!
266+
</esi:except>
267+
</esi:try>
229268
"#;
230269
let ast = parse_document(input).unwrap();
231270
println!("{:?}", ast);

esi/src/vm_types.rs

+68-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ pub unsafe fn read_u64(ptr: *const u8) -> u64 {
44
pub unsafe fn read_u32(ptr: *const u8) -> u32 {
55
ptr.cast::<u32>().read_unaligned()
66
}
7+
pub unsafe fn read_u16(ptr: *const u8) -> u16 {
8+
ptr.cast::<u16>().read_unaligned()
9+
}
710

811
pub const MAGIC: u32 = 0xABADBABA;
912

@@ -15,6 +18,7 @@ pub type Result<T> = std::result::Result<T, VMError>;
1518
pub enum Value {
1619
Null,
1720
LiteralString(*const u8), // Pointer to length-prepended string in code segment
21+
String(Vec<u8>),
1822
Bool(bool),
1923
}
2024

@@ -33,25 +37,79 @@ impl Value {
3337
b"false"
3438
}
3539
}
40+
Value::String(s) => &s,
3641
}
3742
}
3843

3944
pub fn to_bool(&self) -> bool {
4045
match self {
4146
Value::Null => false,
4247
lit_str @ Value::LiteralString(_) => lit_str.to_bytes().len() == 0,
48+
Value::String(s) => s.len() == 0,
4349
Value::Bool(b) => *b,
4450
}
4551
}
52+
53+
pub fn add(self, other: Value) -> Value {
54+
match self {
55+
Value::Null => match other {
56+
Value::Null => Value::Null,
57+
v @ Value::LiteralString(_) => v.clone(),
58+
Value::Bool(_) => todo!(),
59+
Value::String(_) => todo!(),
60+
},
61+
self_lit_str @ Value::LiteralString(self_ptr) => match other {
62+
Value::LiteralString(other_ptr) => unsafe {
63+
let self_len = read_u32(self_ptr) as usize;
64+
let other_len = read_u32(other_ptr) as usize;
65+
let mut out = Vec::with_capacity(self_len + other_len);
66+
let dst = out.as_mut_ptr();
67+
68+
std::ptr::copy_nonoverlapping(self_ptr.add(4), dst, self_len);
69+
std::ptr::copy_nonoverlapping(other_ptr.add(4), dst.add(self_len), other_len);
70+
71+
out.set_len(self_len + other_len);
72+
73+
Value::String(out)
74+
},
75+
Value::Null => self_lit_str.clone(),
76+
Value::Bool(_) => todo!(), // TODO: might need to coerce the bool to a string here
77+
Value::String(other_str) => {
78+
let mut out = Vec::new();
79+
out.extend_from_slice(self_lit_str.to_bytes());
80+
out.extend_from_slice(&other_str);
81+
Value::String(out)
82+
}
83+
},
84+
Value::Bool(self_b) => match other {
85+
Value::Bool(other_b) => todo!(),
86+
Value::Null => todo!(),
87+
Value::LiteralString(_) => todo!(),
88+
Value::String(_) => todo!(),
89+
},
90+
Value::String(self_str) => match other {
91+
Value::Bool(other_b) => todo!(),
92+
Value::Null => todo!(),
93+
other_lit_str @ Value::LiteralString(_) => {
94+
let mut out = Vec::new();
95+
out.extend_from_slice(&self_str);
96+
out.extend_from_slice(other_lit_str.to_bytes());
97+
Value::String(out)
98+
}
99+
Value::String(_) => todo!(),
100+
},
101+
}
102+
}
46103
}
47104

48105
impl PartialEq for Value {
49106
fn eq(&self, other: &Value) -> bool {
50107
match self {
51108
Value::Null => match other {
52109
Value::Null => true,
53-
Value::LiteralString(_) => false,
54-
Value::Bool(_) => false, // TODO: not sure if null == false
110+
Value::LiteralString(_) => false, // TODO: empty string might equal null?
111+
Value::String(_) => false, // TODO: empty string might equal null?
112+
Value::Bool(_) => false, // TODO: not sure if null == false
55113
},
56114
Value::LiteralString(self_ptr) => match other {
57115
Value::LiteralString(other_ptr) => {
@@ -63,13 +121,21 @@ impl PartialEq for Value {
63121
self.to_bytes() == other.to_bytes()
64122
}
65123
}
124+
Value::String(other_str) => self.to_bytes() == *other_str,
66125
Value::Null => false,
67126
Value::Bool(_) => false, // TODO: might need to coerce the bool to a string here
68127
},
69128
Value::Bool(self_b) => match other {
70129
Value::Bool(other_b) => self_b == other_b,
71130
Value::Null => false,
72131
Value::LiteralString(_) => false, // TODO: ditto above re: coercion
132+
Value::String(_) => false, // TODO: ditto above re: coercion
133+
},
134+
Value::String(self_str) => match other {
135+
Value::Bool(other_b) => todo!(),
136+
Value::Null => false,
137+
Value::LiteralString(_) => todo!(),
138+
Value::String(other_str) => self_str == other_str,
73139
},
74140
}
75141
}

0 commit comments

Comments
 (0)