1use smallvec::SmallVec;
2use call_stack::Frame;
3use object_pool::ObjectPool;
4use value::Value;
5use errors::ValidateError;
6
7#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
13pub enum OpCode {
14 Nop,
15 LoadNull,
16 LoadInt(i64),
17 LoadFloat(f64),
18 LoadString(String),
19 LoadBool(bool),
20 LoadThis,
21 Pop,
22 Dup,
23 InitLocal(usize),
24 GetLocal(usize),
25 SetLocal(usize),
26 GetArgument(usize),
27 GetNArguments,
28 GetStatic,
29 SetStatic,
30 GetField,
31 SetField,
32 Call(usize),
33 CallField(usize),
34 Branch(usize),
35 ConditionalBranch(usize, usize),
36 Return,
37 Add,
38 Sub,
39 Mul,
40 Div,
41 Mod,
42 Pow,
43 IntAdd,
44 IntSub,
45 IntMul,
46 IntDiv,
47 IntMod,
48 IntPow,
49 FloatAdd,
50 FloatSub,
51 FloatMul,
52 FloatDiv,
53 FloatPowi,
54 FloatPowf,
55 StringAdd,
56 CastToFloat,
57 CastToInt,
58 CastToBool,
59 CastToString,
60 And,
61 Or,
62 Not,
63 TestLt,
64 TestLe,
65 TestEq,
66 TestNe,
67 TestGe,
68 TestGt,
69 Rotate2,
70 Rotate3,
71 RotateReverse(usize),
72
73 Select(SelectType, Vec<OpCode>, Vec<OpCode>),
76
77 #[serde(skip_serializing, skip_deserializing)]
78 Rt(RtOpCode)
79}
80
81#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
82pub enum SelectType {
83 And,
84 Or
85}
86
87#[derive(Clone, Debug, PartialEq)]
88pub enum RtOpCode {
89 LoadObject(usize),
90 BulkLoad(SmallVec<[Value; 4]>),
91 StackMap(StackMapPattern),
92 ConstCall(ValueLocation , ValueLocation , usize ),
93 ConstGetField(usize , Value )
94}
95
96#[derive(Clone, Debug, PartialEq)]
97pub struct StackMapPattern {
98 pub(crate) map: SmallVec<[ValueLocation; 4]>,
99 pub(crate) end_state: isize
100}
101
102#[derive(Clone, Debug, PartialEq)]
103pub enum ValueLocation {
104 Stack(isize),
105 Local(usize),
106 Argument(usize),
107 ConstInt(i64),
108 ConstFloat(f64),
109 ConstString(String),
110 ConstBool(bool),
111 ConstNull,
112 ConstObject(usize),
113 This
114}
115
116impl StackMapPattern {
117 pub fn to_opcode_sequence(&self) -> Option<Vec<OpCode>> {
118 let mut opcodes: Vec<OpCode> = Vec::new();
119 let n_pushes: isize = self.end_state - self.map.len() as isize;
120 if n_pushes > 0 {
121 return None;
122 } else {
123 for _ in 0..(-n_pushes) {
124 opcodes.push(OpCode::Pop);
125 }
126 }
127
128 for loc in self.map.iter() {
129 if let Some(op) = loc.to_opcode() {
130 opcodes.push(op);
131 } else {
132 return None;
133 }
134 }
135 Some(opcodes)
136 }
137}
138
139impl ValueLocation {
140 pub fn to_opcode(&self) -> Option<OpCode> {
141 match *self {
142 ValueLocation::Stack(_) => None,
143 ValueLocation::Local(id) => Some(OpCode::GetLocal(id)),
144 ValueLocation::Argument(id) => Some(OpCode::GetArgument(id)),
145 ValueLocation::ConstInt(v) => Some(OpCode::LoadInt(v)),
146 ValueLocation::ConstFloat(v) => Some(OpCode::LoadFloat(v)),
147 ValueLocation::ConstString(ref s) => Some(OpCode::LoadString(s.clone())),
148 ValueLocation::ConstBool(v) => Some(OpCode::LoadBool(v)),
149 ValueLocation::ConstNull => Some(OpCode::LoadNull),
150 ValueLocation::ConstObject(id) => Some(OpCode::Rt(RtOpCode::LoadObject(id))),
151 ValueLocation::This => Some(OpCode::LoadThis)
152 }
153 }
154
155 pub fn from_opcode(op: &OpCode) -> Option<ValueLocation> {
156 match *op {
157 OpCode::GetLocal(id) => Some(ValueLocation::Local(id)),
158 OpCode::GetArgument(id) => Some(ValueLocation::Argument(id)),
159 OpCode::LoadInt(v) => Some(ValueLocation::ConstInt(v)),
160 OpCode::LoadFloat(v) => Some(ValueLocation::ConstFloat(v)),
161 OpCode::LoadString(ref v) => Some(ValueLocation::ConstString(v.clone())),
162 OpCode::LoadBool(v) => Some(ValueLocation::ConstBool(v)),
163 OpCode::LoadNull => Some(ValueLocation::ConstNull),
164 OpCode::Rt(RtOpCode::LoadObject(id)) => Some(ValueLocation::ConstObject(id)),
165 OpCode::LoadThis => Some(ValueLocation::This),
166 _ => None
167 }
168 }
169
170 pub fn extract(&self, frame: &Frame, pool: &mut ObjectPool) -> Value {
171 match *self {
172 ValueLocation::Stack(dt) => {
173 let center = frame.exec_stack.len() - 1;
174 frame.exec_stack.get((center as isize + dt) as usize).unwrap()
175 },
176 ValueLocation::Local(id) => frame.get_local(id),
177 ValueLocation::Argument(id) => frame.must_get_argument(id),
178 ValueLocation::ConstString(ref s) => {
179 Value::Object(pool.allocate(Box::new(s.clone())))
180 },
181 ValueLocation::ConstNull => Value::Null,
182 ValueLocation::ConstInt(v) => Value::Int(v),
183 ValueLocation::ConstFloat(v) => Value::Float(v),
184 ValueLocation::ConstBool(v) => Value::Bool(v),
185 ValueLocation::ConstObject(id) => Value::Object(id),
186 ValueLocation::This => frame.get_this()
187 }
188 }
189
190 pub fn to_value(&self) -> Option<Value> {
191 match *self {
192 ValueLocation::ConstNull => Some(Value::Null),
193 ValueLocation::ConstInt(v) => Some(Value::Int(v)),
194 ValueLocation::ConstFloat(v) => Some(Value::Float(v)),
195 ValueLocation::ConstBool(v) => Some(Value::Bool(v)),
196 ValueLocation::ConstObject(v) => Some(Value::Object(v)),
197 _ => None
198 }
199 }
200}
201
202macro_rules! validate_select_opcode_sequence {
203 ($seq:expr) => ({
204 {
205 let mut stack_depth: isize = 0;
206 for op in $seq {
207 op.validate(false)?;
208 let (n_pops, n_pushes) = op.get_stack_depth_change();
209 stack_depth -= n_pops as isize;
210 if stack_depth < 0 {
211 return Err(ValidateError::new("Stack underflow"));
212 }
213 stack_depth += n_pushes as isize;
214 }
215 if stack_depth != 1 {
216 return Err(ValidateError::new("Expecting exactly one value"));
217 }
218 }
219 })
220}
221
222impl OpCode {
223 pub fn modifies_control_flow(&self) -> bool {
224 match *self {
225 OpCode::Branch(_) | OpCode::ConditionalBranch(_, _) | OpCode::Return => true,
226 _ => false
227 }
228 }
229
230 pub fn validate(&self, allow_modify_control_flow: bool) -> Result<(), ValidateError> {
231 if !allow_modify_control_flow {
232 if self.modifies_control_flow() {
233 return Err(ValidateError::new("Modifying control flow is not allowed here"));
234 }
235 }
236
237 match *self {
238 OpCode::RotateReverse(n) => {
239 if n <= 0 {
240 return Err(ValidateError::new("RotateReverse only accepts an operand greater than zero"));
241 }
242 Ok(())
243 },
244 OpCode::Select(_, ref left, ref right) => {
245 validate_select_opcode_sequence!(left);
246 validate_select_opcode_sequence!(right);
247 Ok(())
248 },
249 _ => Ok(())
250 }
251 }
252
253 pub fn from_value(v: Value) -> OpCode {
254 use self::OpCode::*;
255
256 match v {
257 Value::Null => LoadNull,
258 Value::Bool(v) => LoadBool(v),
259 Value::Int(v) => LoadInt(v),
260 Value::Float(v) => LoadFloat(v),
261 Value::Object(v) => Rt(RtOpCode::LoadObject(v))
262 }
263 }
264
265 pub fn to_value(&self) -> Option<Value> {
266 use self::OpCode::*;
267
268 match *self {
269 LoadNull => Some(Value::Null),
270 LoadBool(v) => Some(Value::Bool(v)),
271 LoadInt(v) => Some(Value::Int(v)),
272 LoadFloat(v) => Some(Value::Float(v)),
273 Rt(RtOpCode::LoadObject(id)) => Some(Value::Object(id)),
274 _ => None
275 }
276 }
277
278 pub fn get_stack_depth_change(&self) -> (usize, usize) {
279 use self::OpCode::*;
280
281 match *self {
283 Nop => (0, 0),
284 LoadNull | LoadInt(_) | LoadFloat(_) | LoadString(_) | LoadBool(_) | LoadThis => (0, 1), Pop => (1, 0), Dup => (0, 1), InitLocal(_) => (0, 0),
288 GetLocal(_) => (0, 1), SetLocal(_) => (1, 0), GetArgument(_) => (0, 1), GetNArguments => (0, 1), GetStatic => (1, 1), SetStatic => (2, 0), GetField => (2, 1), SetField => (3, 0), Branch(_) => (0, 0),
297 ConditionalBranch(_, _) => (1, 0), Return => (1, 0), Add | Sub | Mul | Div | Mod | Pow
300 | IntAdd | IntSub | IntMul | IntDiv | IntMod | IntPow
301 | FloatAdd | FloatSub | FloatMul | FloatDiv
302 | FloatPowi | FloatPowf
303 | StringAdd => (2, 1), CastToFloat | CastToInt | CastToBool | CastToString => (1, 1),
305 Not => (1, 1),
306 And | Or => (2, 1), TestLt | TestLe | TestEq | TestNe | TestGe | TestGt => (2, 1), Call(n_args) => (n_args + 2, 1), CallField(n_args) => (n_args + 3, 1), Rotate2 => (2, 2),
311 Rotate3 => (3, 3),
312 RotateReverse(n) => (n, n),
313 Select(_, _, _) => (0, 1), Rt(ref op) => match *op {
315 RtOpCode::LoadObject(_) => (0, 1), RtOpCode::BulkLoad(ref values) => (0, values.len()), RtOpCode::StackMap(ref p) => if p.end_state >= 0 {
318 (0, p.end_state as usize)
319 } else {
320 ((-p.end_state) as usize, 0)
321 },
322 RtOpCode::ConstCall(_, _, n_args) => (n_args, 1), RtOpCode::ConstGetField(_, _) => (0, 1) }
325 }
326 }
327}