Add a block-based -sendCommand... variant to ProtocolClient.
authorRobert Sesek <rsesek@bluestatic.org>
Sat, 10 Oct 2015 15:47:42 +0000 (11:47 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Sat, 10 Oct 2015 17:17:16 +0000 (13:17 -0400)
This starts making ProtocolClient responsible for dispatching debugger messages.

Source/DebuggerBackEnd.m
Source/ProtocolClient.h
Source/ProtocolClient.m

index df26dd89fc81eeb2c88b5db385767b414b98fc6f..942eeb7ef6f5cfad7b30db8f55bcd7ec5d07044e 100644 (file)
 /**
  * Tells the debugger to step into the current command.
  */
-- (void)stepIn
-{
-  NSNumber* tx = [client_ sendCommandWithFormat:@"step_into"];
-  [self recordCallback:@selector(debuggerStep:) forTransaction:tx];
+- (void)stepIn {
+  [client_ sendCommandWithFormat:@"step_into" handler:^(NSXMLDocument* message) {
+    [self debuggerStep:message];
+  }];
 }
 
 /**
     [client_ connectOnPort:port_];
 }
 
-- (void)debuggerEngine:(ProtocolClient*)client receivedMessage:(NSXMLDocument*)message
-{
-  // Check and see if there's an error.
+- (void)protocolClient:(ProtocolClient*)client receivedInitialMessage:(NSXMLDocument*)message {
+  [self handleInitialResponse:message];
+}
+
+- (void)protocolClient:(ProtocolClient*)client receivedErrorMessage:(NSXMLDocument*)message {
   NSArray* error = [[message rootElement] elementsForName:@"error"];
   if ([error count] > 0) {
     NSLog(@"Xdebug error: %@", error);
     NSString* errorMessage = [[[[error objectAtIndex:0] children] objectAtIndex:0] stringValue];
     [self errorEncountered:errorMessage];
   }
+}
 
-  if ([[[message rootElement] name] isEqualToString:@"init"]) {
-    [self handleInitialResponse:message];
-    return;
-  }
-
+- (void)debuggerEngine:(ProtocolClient*)client receivedMessage:(NSXMLDocument*)message {
   [self handleResponse:message];
 }
 
index 698e90e7fa9a5b1747c027920008c5fc2316983b..3f9e91b983104578aefe335dc7890cf22db2cc94 100644 (file)
@@ -20,6 +20,8 @@
 
 @protocol ProtocolClientDelegate;
 
+typedef void (^ProtocolClientMessageHandler)(NSXMLDocument*);
+
 // ProtocolClient sends string commands to a DBGP <http://www.xdebug.org/docs-dbgp.php>
 // debugger engine and receives XML packets in response. This class ensures
 // proper sequencing of the messages.
 // safe and schedules the request on the |runLoop_|.
 - (NSNumber*)sendCommandWithFormat:(NSString*)format, ...;
 
+// Sends a command with the given |format| to the debugger. When a response is
+// received, |handler| is invoked. If an error occurs or the connection is
+// interrupted, the delegate will be notified.
+- (void)sendCommandWithFormat:(NSString*)format
+                      handler:(ProtocolClientMessageHandler)handler,
+                      ...;
+
 // Sends a command to the debugger. The command must have a substring |{txn}|
 // within it, which will be replaced with the transaction ID. Use this if
 // |-sendCommandWithFormat:|'s insertion of the transaction ID is incorrect.
@@ -57,6 +66,7 @@
 @protocol ProtocolClientDelegate
 - (void)debuggerEngineConnected:(ProtocolClient*)client;
 - (void)debuggerEngineDisconnected:(ProtocolClient*)client;
-
+- (void)protocolClient:(ProtocolClient*)client receivedInitialMessage:(NSXMLDocument*)message;
+- (void)protocolClient:(ProtocolClient*)client receivedErrorMessage:(NSXMLDocument*)message;
 - (void)debuggerEngine:(ProtocolClient*)client receivedMessage:(NSXMLDocument*)message;
 @end
index a8e16109228f61d30ab95a0aa06689653ba38603..aa80cb228daabc4ff4b5de6a96fb924ff05b88dc 100644 (file)
   // state of the debugger.
   id<ProtocolClientDelegate> _delegate;  // weak
 
+  // A map between transaction ID and handler block for that message.
+  NSMutableDictionary<NSNumber*, ProtocolClientMessageHandler>* _dispatchTable;
+
   // The next transaction ID to assign.
-  NSInteger _nextID;
+  int _nextID;
 
   // Records the last read and written transaction IDs. These are only used in
   // creating LogEntry objects.
 - (id)initWithDelegate:(id<ProtocolClientDelegate>)delegate {
   if ((self = [super init])) {
     _delegate = delegate;
+    _dispatchTable = [[NSMutableDictionary alloc] init];
   }
   return self;
 }
 
 - (void)dealloc {
+  [_dispatchTable release];
   [super dealloc];
 }
 
   return callbackKey;
 }
 
+- (void)sendCommandWithFormat:(NSString*)format
+                      handler:(ProtocolClientMessageHandler)handler, ... {
+  // Collect varargs and format command.
+  va_list args;
+  va_start(args, handler);
+  NSString* command = [[NSString alloc] initWithFormat:format arguments:args];
+  va_end(args);
+
+  int transaction = _nextID++;
+  NSString* taggedCommand = [NSString stringWithFormat:@"%@ -i %d", [command autorelease], transaction];
+
+  assert(_messageQueue);
+  [_dispatchTable setObject:[[handler copy] autorelease] forKey:@(transaction)];
+  [_messageQueue sendMessage:taggedCommand];
+}
+
 - (NSNumber*)sendCustomCommandWithFormat:(NSString*)format, ... {
   // Collect varargs and format command.
   va_list args;
 - (void)messageQueueDidDisconnect:(MessageQueue*)queue {
   [_messageQueue release];
   _messageQueue = nil;
+  [_dispatchTable removeAllObjects];
   [_delegate debuggerEngineDisconnected:self];
 }
 
 // Callback for when a message has been sent.
-- (void)messageQueue:(MessageQueue*)queue didSendMessage:(NSString*)message
-{
+- (void)messageQueue:(MessageQueue*)queue didSendMessage:(NSString*)message {
   NSInteger tag = [self transactionIDFromCommand:message];
   _lastWrittenID = tag;
 
 }
 
 // Callback with the message content when one has been receieved.
-- (void)messageQueue:(MessageQueue*)queue didReceiveMessage:(NSString*)message
-{
+- (void)messageQueue:(MessageQueue*)queue didReceiveMessage:(NSString*)message {
+  // Record this message in the transaction log.
   LoggingController* logger = [[AppDelegate instance] loggingController];
   LogEntry* entry = [LogEntry newReceiveEntry:message];
   entry.lastReadTransactionID = _lastReadID;
   entry.lastWrittenTransactionID = _lastWrittenID;
   [logger recordEntry:entry];
 
-  // Test if we can convert it into an NSXMLDocument.
+  // Parse the XML and test for errors.
   NSError* error = nil;
   NSXMLDocument* xml = [[NSXMLDocument alloc] initWithXMLString:message
                                                         options:NSXMLDocumentTidyXML
     [self messageQueue:queue error:error];
     return;
   }
+  int transactionID = [self transactionIDFromResponse:xml];
 
-  _lastReadID = [self transactionIDFromResponse:xml];
+  _lastReadID = transactionID;
   entry.lastReadTransactionID = _lastReadID;
 
-  [_delegate debuggerEngine:self receivedMessage:xml];
+  if ([[[xml rootElement] elementsForName:@"error"] count] > 0) {
+    // Handle back-end errors.
+    [_delegate protocolClient:self receivedErrorMessage:xml];
+  } else if ([[[xml rootElement] name] isEqualToString:@"init"]) {
+    // Handle the initial connection message.
+    [_delegate protocolClient:self receivedInitialMessage:xml];
+  } else {
+    // Dispatch the handler for the message.
+    ProtocolClientMessageHandler handler = [_dispatchTable objectForKey:@(transactionID)];
+    if (handler) {
+      handler(xml);
+      [_dispatchTable removeObjectForKey:@(transactionID)];
+    } else {
+      // TODO(rsesek): Remove this path once the backend rewrite is complete.
+      [_delegate debuggerEngine:self receivedMessage:xml];
+    }
+  }
 }
 
 @end