Kaynağa Gözat

Parse DefReturn and propagate return values correctly

Isaac Woods 5 yıl önce
ebeveyn
işleme
1a5ed91778

+ 9 - 0
aml_parser/src/lib.rs

@@ -86,6 +86,15 @@ pub enum AmlError {
      */
     /// Produced when a path is given that does not point to an object in the AML namespace.
     ObjectDoesNotExist(String),
+
+    /*
+     * Errors produced executing control methods.
+     */
+    /// This is not a real error, but is used to propagate return values from within the deep
+    /// parsing call-stack. It should only be emitted when parsing a `DefReturn`. We use the
+    /// error system here because the way errors are propagated matches how we want to handle
+    /// return values.
+    Return(AmlValue),
 }
 
 #[derive(Debug)]

+ 1 - 0
aml_parser/src/opcode.rs

@@ -37,6 +37,7 @@ pub const EXT_DEF_PROCESSOR_OP: u8 = 0x83;
  */
 pub const DEF_IF_ELSE_OP: u8 = 0xa0;
 pub const DEF_ELSE_OP: u8 = 0xa1;
+pub const DEF_RETURN_OP: u8 = 0xa4;
 
 /*
  * Type 2 opcodes

+ 25 - 2
aml_parser/src/type1.rs

@@ -16,7 +16,7 @@ where
      *                DefNotify | DefRelease | DefReset | DefReturn | DefSignal | DefSleep | DefStall |
      *                DefWhile
      */
-    comment_scope_verbose("Type1Opcode", choice!(def_if_else())
+    comment_scope_verbose("Type1Opcode", choice!(def_if_else(), def_return()))
 }
 
 fn def_if_else<'a, 'c>() -> impl Parser<'a, 'c, ()>
@@ -55,7 +55,6 @@ where
                 ))
                 .map_with_context(|((predicate, then_branch), else_branch), context| {
                     let branch = if predicate { then_branch } else { else_branch };
-                    info!("Executing branch: {:x?}", branch);
 
                     match term_list(PkgLength::from_raw_length(branch, branch.len() as u32))
                         .parse(branch, context)
@@ -67,3 +66,27 @@ where
         ))
         .discard_result()
 }
+
+fn def_return<'a, 'c>() -> impl Parser<'a, 'c, ()>
+where
+    'c: 'a,
+{
+    /*
+     * DefReturn := 0xa4 ArgObject
+     * ArgObject := TermArg => DataRefObject
+     */
+    opcode(opcode::DEF_RETURN_OP)
+        .then(comment_scope_verbose(
+            "DefReturn",
+            term_arg().map(|return_arg| -> Result<(), AmlError> {
+                /*
+                 * To return a value, we want to halt execution of the method and propagate the
+                 * return value all the way up to the start of the method invocation. To do this,
+                 * we emit a special error that is intercepted during method invocation and turned
+                 * into a valid result.
+                 */
+                Err(AmlError::Return(return_arg))
+            }),
+        ))
+        .discard_result()
+}

+ 11 - 6
aml_parser/src/value.rs

@@ -168,10 +168,16 @@ impl AmlValue {
             context.local_6 = None;
             context.local_7 = None;
 
-            match term_list(PkgLength::from_raw_length(code, code.len() as u32)).parse(code, context) {
-                Ok((remaining, context, result)) => {}
-                Err((remaining, context, err)) => error!("Failed to execute control method: {:?}", err),
-            }
+            let return_value =
+                match term_list(PkgLength::from_raw_length(code, code.len() as u32)).parse(code, context) {
+                    // If the method doesn't return a value, we implicitly return `0`
+                    Ok((remaining, context, result)) => Ok(AmlValue::Integer(0)),
+                    Err((remaining, context, AmlError::Return(result))) => Ok(result),
+                    Err((remaining, context, err)) => {
+                        error!("Failed to execute control method: {:?}", err);
+                        Err(err)
+                    }
+                };
 
             /*
              * Now clear the state.
@@ -186,8 +192,7 @@ impl AmlValue {
             context.local_6 = None;
             context.local_7 = None;
 
-            // TODO: return the real return value
-            Ok(AmlValue::Integer(0))
+            return_value
         } else {
             Err(AmlError::IncompatibleValueConversion)
         }