add parsing and type-checking of protocol state machines in preparation for codegen of dynamic FSM checker. fix a couple of bugs here and there.
authorChris Jones <jones.chris.g@gmail.com>
Wed, 08 Jul 2009 18:03:56 -0500
changeset 35763 c9176c61346b625798d12c103b48f6a71ffbbd02
parent 35762 8524cccb39eb3b28e5e17542d8e30921459b1942
child 35764 5e73cb310bddace1b684274e32c2f0b66fe26ed2
push id10694
push userbsmedberg@mozilla.com
push dateMon, 14 Dec 2009 15:23:10 +0000
treeherdermozilla-central@683dfdc4adf0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.2a1pre
add parsing and type-checking of protocol state machines in preparation for codegen of dynamic FSM checker. fix a couple of bugs here and there.
dom/ipc/IFrameEmbeddingProtocol.h
dom/ipc/IFrameEmbeddingProtocolChild.h
dom/plugins/NPAPIProtocol.h
dom/plugins/NPAPIProtocolChild.h
dom/plugins/NPPProtocol.h
dom/plugins/NPPProtocolChild.h
dom/plugins/NPPProtocolParent.h
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/parser.py
ipc/ipdl/ipdl/type.py
--- a/dom/ipc/IFrameEmbeddingProtocol.h
+++ b/dom/ipc/IFrameEmbeddingProtocol.h
@@ -11,29 +11,33 @@
 #include "IPC/IPCMessageUtils.h"
 #include "mozilla/ipc/MessageTypes.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
 namespace IFrameEmbeddingProtocol {
 
 
 enum State {
+    StateStart = 0,
+    StateError,
+    StateLast
 };
 
-enum IFrameEmbeddingProtocolMsgType {
+enum MessageType {
     IFrameEmbeddingProtocolStart = IFrameEmbeddingProtocolMsgStart << 12,
     IFrameEmbeddingProtocolPreStart = (IFrameEmbeddingProtocolMsgStart << 12) - 1,
     Msg_init__ID,
     Reply_init__ID,
     Msg_loadURL__ID,
     Reply_loadURL__ID,
     Msg_move__ID,
     Reply_move__ID,
     IFrameEmbeddingProtocolEnd
 };
+
 class Msg_init :
     public IPC::Message
 {
 private:
     typedef mozilla::ipc::String String;
     typedef mozilla::ipc::StringArray StringArray;
 
 public:
--- a/dom/ipc/IFrameEmbeddingProtocolChild.h
+++ b/dom/ipc/IFrameEmbeddingProtocolChild.h
@@ -81,32 +81,32 @@ public:
         switch (msg.type()) {
         case IFrameEmbeddingProtocol::Msg_init__ID:
             {
                 MagicWindowHandle parentWidget;
 
                 if (!(IFrameEmbeddingProtocol::Msg_init::Read(&(msg), &(parentWidget)))) {
                     return MsgPayloadError;
                 }
-                if (Answerinit(parentWidget)) {
+                if (NS_FAILED(Answerinit(parentWidget))) {
                     return MsgValueError;
                 }
 
                 reply = new IFrameEmbeddingProtocol::Reply_init();
                 reply->set_reply();
                 return MsgProcessed;
             }
         case IFrameEmbeddingProtocol::Msg_loadURL__ID:
             {
                 String uri;
 
                 if (!(IFrameEmbeddingProtocol::Msg_loadURL::Read(&(msg), &(uri)))) {
                     return MsgPayloadError;
                 }
-                if (AnswerloadURL(uri)) {
+                if (NS_FAILED(AnswerloadURL(uri))) {
                     return MsgValueError;
                 }
 
                 reply = new IFrameEmbeddingProtocol::Reply_loadURL();
                 reply->set_reply();
                 return MsgProcessed;
             }
         case IFrameEmbeddingProtocol::Msg_move__ID:
@@ -114,17 +114,17 @@ public:
                 PRUint32 x;
                 PRUint32 y;
                 PRUint32 width;
                 PRUint32 height;
 
                 if (!(IFrameEmbeddingProtocol::Msg_move::Read(&(msg), &(x), &(y), &(width), &(height)))) {
                     return MsgPayloadError;
                 }
-                if (Answermove(x, y, width, height)) {
+                if (NS_FAILED(Answermove(x, y, width, height))) {
                     return MsgValueError;
                 }
 
                 reply = new IFrameEmbeddingProtocol::Reply_move();
                 reply->set_reply();
                 return MsgProcessed;
             }
         default:
--- a/dom/plugins/NPAPIProtocol.h
+++ b/dom/plugins/NPAPIProtocol.h
@@ -15,29 +15,33 @@
 #include "mozilla/plugins/NPPProtocol.h"
 
 namespace mozilla {
 namespace plugins {
 namespace NPAPIProtocol {
 
 
 enum State {
+    StateStart = 0,
+    StateError,
+    StateLast
 };
 
-enum NPAPIProtocolMsgType {
+enum MessageType {
     NPAPIProtocolStart = NPAPIProtocolMsgStart << 12,
     NPAPIProtocolPreStart = (NPAPIProtocolMsgStart << 12) - 1,
     Msg_NP_Initialize__ID,
     Reply_NP_Initialize__ID,
     Msg_NPPConstructor__ID,
     Reply_NPPConstructor__ID,
     Msg_NPPDestructor__ID,
     Reply_NPPDestructor__ID,
     NPAPIProtocolEnd
 };
+
 class Msg_NP_Initialize :
     public IPC::Message
 {
 private:
     typedef mozilla::ipc::String String;
     typedef mozilla::ipc::StringArray StringArray;
 
 public:
--- a/dom/plugins/NPAPIProtocolChild.h
+++ b/dom/plugins/NPAPIProtocolChild.h
@@ -100,17 +100,17 @@ public:
         switch (msg.type()) {
         case NPAPIProtocol::Msg_NP_Initialize__ID:
             {
                 NPError rv;
 
                 if (!(NPAPIProtocol::Msg_NP_Initialize::Read(&(msg)))) {
                     return MsgPayloadError;
                 }
-                if (AnswerNP_Initialize(&(rv))) {
+                if (NS_FAILED(AnswerNP_Initialize(&(rv)))) {
                     return MsgValueError;
                 }
 
                 reply = new NPAPIProtocol::Reply_NP_Initialize(rv);
                 reply->set_reply();
                 return MsgProcessed;
             }
         case NPAPIProtocol::Msg_NPPConstructor__ID:
@@ -149,17 +149,17 @@ public:
                 if (!(NPAPIProtocol::Msg_NPPDestructor::Read(&(msg), &(__ah)))) {
                     return MsgPayloadError;
                 }
                 NPPProtocolChild* __a;
                 __a = static_cast<NPPProtocolChild*>(Lookup(__ah.mChildId));
                 if (!(__a)) {
                     return MsgValueError;
                 }
-                if (NPPDestructor(__a, &(rv))) {
+                if (NS_FAILED(NPPDestructor(__a, &(rv)))) {
                     return MsgValueError;
                 }
                 Unregister(__ah.mChildId);
                 __ah.mChildId = -1;
 
                 reply = new NPAPIProtocol::Reply_NPPDestructor(rv, __ah);
                 reply->set_reply();
                 return MsgProcessed;
--- a/dom/plugins/NPPProtocol.h
+++ b/dom/plugins/NPPProtocol.h
@@ -15,29 +15,33 @@
 #include "mozilla/plugins/NPAPIProtocol.h"
 
 namespace mozilla {
 namespace plugins {
 namespace NPPProtocol {
 
 
 enum State {
+    StateStart = 0,
+    StateError,
+    StateLast
 };
 
-enum NPPProtocolMsgType {
+enum MessageType {
     NPPProtocolStart = NPPProtocolMsgStart << 12,
     NPPProtocolPreStart = (NPPProtocolMsgStart << 12) - 1,
     Msg_NPP_SetWindow__ID,
     Reply_NPP_SetWindow__ID,
     Msg_NPP_GetValue__ID,
     Reply_NPP_GetValue__ID,
     Msg_NPN_GetValue__ID,
     Reply_NPN_GetValue__ID,
     NPPProtocolEnd
 };
+
 class Msg_NPP_SetWindow :
     public IPC::Message
 {
 private:
     typedef mozilla::ipc::String String;
     typedef mozilla::ipc::StringArray StringArray;
 
 public:
--- a/dom/plugins/NPPProtocolChild.h
+++ b/dom/plugins/NPPProtocolChild.h
@@ -89,33 +89,33 @@ public:
         case NPPProtocol::Msg_NPP_SetWindow__ID:
             {
                 NPWindow window;
                 NPError rv;
 
                 if (!(NPPProtocol::Msg_NPP_SetWindow::Read(&(msg), &(window)))) {
                     return MsgPayloadError;
                 }
-                if (AnswerNPP_SetWindow(window, &(rv))) {
+                if (NS_FAILED(AnswerNPP_SetWindow(window, &(rv)))) {
                     return MsgValueError;
                 }
 
                 reply = new NPPProtocol::Reply_NPP_SetWindow(rv);
                 reply->set_reply();
                 return MsgProcessed;
             }
         case NPPProtocol::Msg_NPP_GetValue__ID:
             {
                 String key;
                 String value;
 
                 if (!(NPPProtocol::Msg_NPP_GetValue::Read(&(msg), &(key)))) {
                     return MsgPayloadError;
                 }
-                if (AnswerNPP_GetValue(key, &(value))) {
+                if (NS_FAILED(AnswerNPP_GetValue(key, &(value)))) {
                     return MsgValueError;
                 }
 
                 reply = new NPPProtocol::Reply_NPP_GetValue(value);
                 reply->set_reply();
                 return MsgProcessed;
             }
         default:
--- a/dom/plugins/NPPProtocolParent.h
+++ b/dom/plugins/NPPProtocolParent.h
@@ -103,17 +103,17 @@ public:
         case NPPProtocol::Msg_NPN_GetValue__ID:
             {
                 String key;
                 String value;
 
                 if (!(NPPProtocol::Msg_NPN_GetValue::Read(&(msg), &(key)))) {
                     return MsgPayloadError;
                 }
-                if (AnswerNPN_GetValue(key, &(value))) {
+                if (NS_FAILED(AnswerNPN_GetValue(key, &(value)))) {
                     return MsgValueError;
                 }
 
                 reply = new NPPProtocol::Reply_NPN_GetValue(value);
                 reply->set_reply();
                 return MsgProcessed;
             }
         default:
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -79,16 +79,27 @@ class Visitor:
         pass
 
     def visitMessageDecl(self, md):
         for inParam in md.inParams:
             inParam.accept(self)
         for outParam in md.outParams:
             outParam.accept(self)
 
+    def visitTransitionStmt(self, ts):
+        ts.state.accept(self)
+        for trans in ts.transitions:
+            trans.accept(self)
+
+    def visitTransition(self, t):
+        t.toState.accept(self)
+
+    def visitState(self, s):
+        pass
+
     def visitParam(self, decl):
         pass
 
     def visitTypeSpec(self, ts):
         pass
 
     def visitDecl(self, d):
         pass
@@ -146,41 +157,67 @@ class ProtocolInclude(Node):
     def __init__(self, loc, protocolFile):
         Node.__init__(self, loc)
         self.file = protocolFile
 
 class UsingStmt(Node):
     def __init__(self, loc, cxxTypeSpec):
         Node.__init__(self, loc)
         self.type = cxxTypeSpec
-        
+
 # "singletons"
 class ASYNC:
-    pretty = 'Async'
+    pretty = 'async'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+    @classmethod
+    def __str__(cls):  return cls.pretty
 class RPC:
-    pretty = 'Rpc'
+    pretty = 'rpc'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+    @classmethod
+    def __str__(cls):  return cls.pretty
 class SYNC:
-    pretty = 'Sync'
+    pretty = 'sync'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+    @classmethod
+    def __str__(cls):  return cls.pretty
 
 class INOUT:
-    pretty = 'InOut'
+    pretty = 'inout'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+    @classmethod
+    def __str__(cls):  return cls.pretty
 class IN:
+    pretty = 'in'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+    @classmethod
+    def __str__(cls):  return cls.pretty
     @staticmethod
-    def pretty(ss): return _prettyTable['In'][ss.pretty]
+    def prettySS(cls, ss): return _prettyTable['in'][ss.pretty]
 class OUT:
+    pretty = 'out'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+    @classmethod
+    def __str__(cls):  return cls.pretty
     @staticmethod
-    def pretty(ss): return _prettyTable['Out'][ss.pretty]
+    def pretty(ss): return _prettyTable['out'][ss.pretty]
 
 _prettyTable = {
-    'In'  : { 'Async': 'AsyncRecv',
-             'Sync': 'SyncRecv',
-             'Rpc': 'RpcAnswer' },
-    'Out' : { 'Async': 'AsyncSend',
-              'Sync': 'SyncSend',
-              'Rpc': 'RpcCall' }
+    IN  : { 'async': 'AsyncRecv',
+            'sync': 'SyncRecv',
+            'rpc': 'RpcAnswer' },
+    OUT : { 'async': 'AsyncSend',
+            'sync': 'SyncSend',
+            'rpc': 'RpcCall' }
     # inout doesn't make sense here
 }
 
 
 class Protocol(Node):
     def __init__(self, loc):
         Node.__init__(self, loc)
         self.name = None
@@ -230,16 +267,55 @@ class MessageDecl(Node):
         self.inParams += inParamsList
 
     def addOutParams(self, outParamsList):
         self.outParams += outParamsList
 
     def hasReply(self):
         return self.sendSemantics is SYNC or self.sendSemantics is RPC
 
+class TransitionStmt(Node):
+    def __init__(self, loc, state, transitions):
+        Node.__init__(self, loc)
+        self.state = state
+        self.transitions = transitions
+
+class Transition(Node):
+    def __init__(self, loc, trigger, msg, toState):
+        Node.__init__(self, loc)
+        self.trigger = trigger
+        self.msg = msg
+        self.toState = toState
+
+    @staticmethod
+    def nameToTrigger(name):
+        return { 'send': SEND, 'recv': RECV, 'call': CALL, 'answer': ANSWER }[name]
+
+class SEND:
+    pretty = 'send'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+class RECV:
+    pretty = 'recv'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+class CALL:
+    pretty = 'call'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+class ANSWER:
+    pretty = 'answer'
+    @classmethod
+    def __hash__(cls): return hash(cls.pretty)
+
+class State(Node):
+    def __init__(self, loc, name):
+        Node.__init__(self, loc)
+        self.name = name
+
 class Param(Node):
     def __init__(self, loc, typespec, name):
         Node.__init__(self, loc)
         self.name = name
         self.typespec = typespec
 
 class TypeSpec(Node):
     def __init__(self, loc, spec):
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -92,17 +92,16 @@ class GenerateProtocolHeader(Visitor):
 
     def typedef(self, t, name):
         self.typedefs.append(cxx.Typedef(cxx.Type(t), cxx.Type(name)))
 
     def injectTypedefs(self, scope):
         for tdef in self.typedefs:
             scope.addstmt(tdef)
 
-
     def visitTranslationUnit(self, tu):
         f = self.file
 
         f.addthing(cxx.Whitespace('''//
 // Automatically generated by ipdlc.
 // Edit at your own risk
 //
 
@@ -155,40 +154,51 @@ class GenerateProtocolHeader(Visitor):
                 self.ns = innerns
 
         ns = cxx.Namespace(self.pname)
         self.ns.addstmt(ns)
         self.ns = ns
         ns.addstmt(cxx.Whitespace.NL)
         ns.addstmt(cxx.Whitespace.NL)
 
+        # state information
+        stateenum = cxx.TypeEnum('State')
+        for ts in p.transitionStmts:
+            ts.accept(self)
+            stateenum.addId(ts.state.decl._cxxname)
+        if len(p.transitionStmts):
+            startstate = p.transitionStmts[0].state.decl._cxxname
+        else:
+            startstate = '0'
+        stateenum.addId('StateStart', startstate)
+        stateenum.addId('StateError')
+        stateenum.addId('StateLast')
+
+        ns.addstmt(cxx.StmtDecl(cxx.Decl(stateenum, '')))
+        ns.addstmt(cxx.Whitespace.NL)
+
         # previsit the messages and stash away some common info used
         # several times later
         for md in p.messageDecls:
             md.accept(self)
 
-        # TODO
-        for ts in p.transitionStmts:
-            ts.accept(self)
-        ns.addstmt(cxx.StmtDecl(cxx.Decl(cxx.TypeEnum('State'), '')))
-        ns.addstmt(cxx.Whitespace.NL)
-
         # spit out message type enum and classes
         msgstart = self.pname +'MsgStart << 12'
-        msgenum = cxx.TypeEnum(self.pname +'MsgType')
+        msgenum = cxx.TypeEnum('MessageType')
         msgenum.addId(self.pname +'Start', msgstart)
         msgenum.addId(self.pname +'PreStart', '('+ msgstart +') - 1')
 
         for md in p.messageDecls:
             msgenum.addId(md._cxx.id +'__ID')
             if md.decl.type.hasReply():
                 msgenum.addId(md._cxx.replyid +'__ID')            
 
         msgenum.addId(self.pname +'End')
         ns.addstmt(cxx.StmtDecl(cxx.Decl(msgenum, '')))
+        ns.addstmt(cxx.Whitespace.NL)
 
         for md in p.messageDecls:
             ns.addstmt(generateMessageClass(md, self.injectTypedefs))
             if md.decl.type.hasReply():
                 ns.addstmt(generateReplyClass(md, self.injectTypedefs))
 
         ns.addstmt(cxx.Whitespace.NL)
         ns.addstmt(cxx.Whitespace.NL)
@@ -227,16 +237,20 @@ class GenerateProtocolHeader(Visitor):
         # the ID is used by the IPC layer only
         md._cxx.id = 'Msg_%s'% (md.decl.progname)
         md._cxx.nsid = '%s::%s'% (self.pname, md._cxx.id)
         if md.decl.type.hasReply():
             md._cxx.replyid = 'Reply_%s'% (md.decl.progname)
             md._cxx.nsreplyid = '%s::%s'% (self.pname, md._cxx.replyid)
 
 
+    def visitTransitionStmt(self, ts):
+        ts.state.decl._cxxname = 'State_%s__ID'% (ts.state.decl.progname)
+
+
 def generateMsgClass(md, clsname, params, typedefInjector):
         cls = cxx.Class(name=clsname,
                         inherits=[ cxx.Inherit('IPC::Message') ])
         cls.addstmt(cxx.Label('private'))
         typedefInjector(cls)
         cls.addstmt(cxx.Whitespace.NL)
 
         cls.addstmt(cxx.Label('public'))
@@ -329,22 +343,21 @@ def generateMessageClass(md, typedefInje
     return generateMsgClass(md, md._cxx.id, md._cxx.params, typedefInjector)
 
 def generateReplyClass(md, typedefInjector):
     return generateMsgClass(md, md._cxx.replyid, md._cxx.returns, typedefInjector)
 
 
 ##-----------------------------------------------------------------------------
 _channelTable = {
-    'Async': [ 'mozilla', 'ipc', 'AsyncChannel' ],
-    'Sync': [ 'mozilla', 'ipc', 'SyncChannel' ],
-    'Rpc': [ 'mozilla', 'ipc', 'RPCChannel' ]
+    ASYNC: [ 'mozilla', 'ipc', 'AsyncChannel' ],
+    SYNC: [ 'mozilla', 'ipc', 'SyncChannel' ],
+    RPC: [ 'mozilla', 'ipc', 'RPCChannel' ]
 }
 
-
 class GenerateProtocolActorHeader(Visitor):
     def __init__(self, myside, otherside):
         self.myside = myside  # "Parent" or "Child"
         self.otherside = otherside
         self.clsname = None
         self.pname = None
         self.file = None
         self.ns = None
@@ -391,17 +404,17 @@ class GenerateProtocolActorHeader(Visito
                 p, _protocolHeaderName(p.name)+ self.myside)
             self.file.addthing(cxx.CppDirective('include', '"'+ header +'"'))
 
 
     def visitProtocol(self, p):
         if p.decl.type.isManager():
             self.file.addthing(cxx.CppDirective('include', '"base/id_map.h"'))
 
-        channel = _channelTable[p.decl.type.sendSemantics.pretty]
+        channel = _channelTable[p.decl.type.sendSemantics]
         channelname = '::'.join(channel)
         channelfile = '/'.join(channel) +'.h'
         if p.decl.type.isToplevel():
             self.channelsel = '.'
         else:
             self.channelsel = '->'
 
         self.file.addthing(cxx.CppDirective('include', '"'+ channelfile +'"'))
--- a/ipc/ipdl/ipdl/parser.py
+++ b/ipc/ipdl/ipdl/parser.py
@@ -143,17 +143,17 @@ reserved = set((
         'transfer',
         'using'))
 tokens = [
     'COLONCOLON', 'ID', 'STRING'
 ] + [ r.upper() for r in reserved ]
 
 t_COLONCOLON = '::'
 
-literals = '(){}[];,~'
+literals = '(){}[];:,~'
 t_ignore = ' \f\t\v'
 
 def t_linecomment(t):
     r'//[^\n]*'
 
 def t_multilinecomment(t):
     r'/\*(\n|.)*?\*/'
     t.lexer.lineno += t.value.count('\n')
@@ -170,17 +170,17 @@ def t_ID(t):
 
 def t_STRING(t):
     r'"[^"\n]*"'
     t.value = t.value[1:-1]
     return t
 
 def t_error(t):
     includeStackStr = Parser.includeStackString()
-    raise Exception, '%s%s: lexically invalid characters %s'% (
+    raise Exception, '%s%s: error: lexically invalid characters %s'% (
         includeStackStr, Loc(Parser.current.filename, t.lineno), str(t))
 
 ##-----------------------------------------------------------------------------
 
 def p_TranslationUnit(p):
     """TranslationUnit : Preamble NamespacedProtocolDefn"""
     tu = Parser.current.tu
     for stmt in p[1]:
@@ -314,20 +314,59 @@ def p_MessageInParams(p):
 def p_MessageOutParams(p):
     """MessageOutParams : RETURNS '(' ParamList ')'
                         | """
     if 1 == len(p):
         p[0] = [ ]
     else:
         p[0] = p[3]
 
+##--------------------
+## State machine
+
 def p_TransitionStmts(p):
-    """TransitionStmts : """
-    # FIXME/cjones: impl
-    p[0] = [ ]
+    """TransitionStmts : TransitionStmts TransitionStmt
+                       | TransitionStmt
+                       | """
+    if 3 == len(p):
+        p[1].append(p[2])
+        p[0] = p[1]
+    elif 2 == len(p):
+        p[0] = [ p[1] ]
+    else:
+        p[0] = [ ]
+
+def p_TransitionStmt(p):
+    """TransitionStmt : State ':' Transitions"""
+    p[0] = TransitionStmt(locFromTok(p, 1), p[1], p[3])
+
+def p_Transitions(p):
+    """Transitions : Transitions Transition
+                   | Transition"""
+    if 3 == len(p):
+        p[1].append(p[2])
+        p[0] = p[1]
+    else:
+        p[0] = [ p[1] ]
+
+def p_Transition(p):
+    """Transition : Trigger MessageId GOTO State ';'"""
+    loc, trigger = p[1]
+    p[0] = Transition(loc, trigger, p[2], p[4])
+
+def p_Trigger(p):
+    """Trigger : SEND
+               | RECV
+               | CALL
+               | ANSWER"""
+    p[0] = [ locFromTok(p, 1), Transition.nameToTrigger(p[1]) ]
+
+def p_State(p):
+    """State : ID"""
+    p[0] = State(locFromTok(p, 1), p[1])
 
 ##--------------------
 ## Minor stuff
 def p_SendSemanticsQual(p):
     """SendSemanticsQual : ASYNC
                          | RPC
                          | SYNC
                          | """
@@ -389,10 +428,10 @@ def p_QualifiedID(p):
     if isinstance(p[1], QualifiedId):
         p[1].qualify(p[3])
         p[0] = p[1]
     else:
         p[0] = QualifiedId(locFromTok(p, 1), p[3])
 
 def p_error(t):
     includeStackStr = Parser.includeStackString()
-    raise Exception, '%s%s: syntax error near "%s"'% (
+    raise Exception, '%s%s: error: bad syntax near "%s"'% (
         includeStackStr, Loc(Parser.current.filename, t.lineno), t.value)
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -27,17 +27,17 @@
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 import sys
 
-from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, TypeSpec, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT
+from ipdl.ast import CxxInclude, Decl, Loc, QualifiedId, TypeSpec, UsingStmt, Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT, ANSWER, CALL, RECV, SEND
 import ipdl.builtin as builtin
 
 class Type:
     # Is this a C++ type?
     def isCxx():
         return False
     # Is this an IPDL type?
     def isIPDL():
@@ -109,32 +109,38 @@ class ImportedCxxType(CxxType):
 class GeneratedCxxType(CxxType):
     def isGenerated(self): return True
     def isVisible(self):   return False
 
 ##--------------------
 class IPDLType(Type):
     def isIPDL(self):  return True
     def isVisible(self): return True
+    def isState(self): return False
+    def isMessage(self): return False
     def isProtocol(self): return False
 
     def isAsync(self): return self.sendSemantics is ASYNC
     def isSync(self): return self.sendSemantics is SYNC
     def isRpc(self): return self.sendSemantics is RPC
 
     def talksAsync(self): return True
     def talksSync(self): return self.isSync() or self.isRpc()
     def talksRpc(self): return self.isRpc()
 
     def hasReply(self):  return self.isSync() or self.isRpc()
 
     def needsMoreJuiceThan(self, o):
         return (o.isAsync() and not self.isAsync()
                 or o.isSync() and self.isRpc())
 
+class StateType(IPDLType):
+    def __init__(self): pass
+    def isState(self): return True
+
 class MessageType(IPDLType):
     def __init__(self, sendSemantics, direction,
                  ctor=False, dtor=False, cdtype=None):
         assert not (ctor and dtor)
         assert not (ctor or dtor) or type is not None
 
         self.sendSemantics = sendSemantics
         self.direction = direction
@@ -314,21 +320,21 @@ class GatherDecls(Visitor):
         # all TranslationUnits declare symbols in global scope
         if hasattr(tu, '_tchecked') and tu._tchecked:
             return
         tu._tchecked = True
         self.depth += 1
 
         # bit of a hack here --- we want the builtin |using|
         # statements to be added to the symbol table before anything
-        # else, but we also want them in the top-level translation
-        # unit's list of using stmts so that we can use them later
-        # down the pipe.  so we add them to the symbol table before
-        # anything else, and prepend them to the top-level TU after
-        # it's visited all its |using| decls
+        # else, but we also want them in the translation units' list
+        # of using stmts so that we can use them later down the pipe.
+        # so we add them to the symbol table before anything else, and
+        # prepend them to the TUs after visiting all their |using|
+        # decls
         if 1 == self.depth:
             for using in self.builtinUsing:
                 udecl = Decl(using.loc)
                 udecl.shortname = using.type.basename()
                 fullname = str(using.type)
                 if udecl.shortname != fullname:
                     udecl.fullname = fullname
                 udecl.type = BuiltinCxxType(using.type.spec)
@@ -363,18 +369,17 @@ class GatherDecls(Visitor):
         # each protocol has its own scope for types brought in through |using|
         self.symtab.enterScope(tu)
 
         # and for all imported C++ types
         for using in tu.using:
             using.accept(self)
 
         # (see long comment above)
-        if 1 == self.depth:
-            tu.using = self.builtinUsing + tu.using
+        tu.using = self.builtinUsing + tu.using
 
         # grab symbols in the protocol itself
         p.accept(self)
 
         self.symtab.exitScope(tu)
 
         tu.type = VOID
         self.depth -= 1
@@ -418,16 +423,25 @@ class GatherDecls(Visitor):
             if not(ctordecl and dtordecl
                    and ctordecl.type.isCtor() and dtordecl.type.isDtor()):
                 self.errors.append(
                     errormsg(
                         managed.loc,
                         "constructor and destructor declarations are required for managed protocol `%s' (managed by protocol `%s')",
                         mgdname, p.name))
 
+        # declare each state before decorating their mention
+        for trans in p.transitionStmts:
+            sdecl = Decl(trans.state.loc)
+            sdecl.progname = trans.state.name
+            sdecl.type = StateType()
+
+            self.symtab.declare(sdecl)
+            trans.state.decl = sdecl
+
         for trans in p.transitionStmts:
             trans.accept(self)
 
         # FIXME/cjones declare all the little C++ thingies that will
         # be generated.  they're not relevant to IPDL itself, but
         # those ("invisible") symbols can clash with others in the
         # IPDL spec, and we'd like to catch those before C++ compilers
         # are allowed to obfuscate the error
@@ -590,16 +604,48 @@ class GatherDecls(Visitor):
         decl.progname = msgname
         decl.type = msgtype
 
         self.symtab.declare(decl)
         md.decl = decl
         md.protocolDecl = self.currentProtocolDecl
 
 
+    def visitTransition(self, t):
+        loc = t.loc
+
+        sname = t.toState.name
+        sdecl = self.symtab.lookup(sname)
+        if sdecl is None:
+            self.errors.append(
+                errormsg(loc, "state `%s' has not been declared", sname))
+        elif not sdecl.type.isState():
+            self.errors.append(
+                errormsg(
+                    loc,
+                    "`%s' should have state type, but instead has type `%s'",
+                    sname, sdecl.type.typename()))
+        else:
+            t.toState.decl = sdecl
+
+        mname = t.msg
+        mdecl = self.symtab.lookup(mname)
+        if mdecl is None:
+            self.errors.append(
+                errormsg(loc, "message `%s' has not been declared", mname))
+        elif not mdecl.type.isMessage():
+            self.errors.append(
+                errormsg(
+                    loc,
+                    "`%s' should have message type, but instead has type `%s'",
+                    mname, mdecl.type.typename()))
+        else:
+            t.msg = mdecl
+
+
 class CheckTypes(Visitor):
     def __init__(self, symtab, errors):
         self.symtab = symtab
         self.errors = errors
         self.visited = set()
 
     def visitProtocolInclude(self, inc):
         if inc.tu.filename in self.visited:
@@ -688,8 +734,30 @@ class CheckTypes(Visitor):
                     mname))
 
         if (mtype.isCtor() or mtype.isDtor()) and not ptype.isManagerOf(mtype.constructedType()):
             self.errors.append(errormsg(
                     loc,
                     "ctor/dtor for protocol `%s', which is not managed by protocol `%s'", 
                     mname[:-len('constructor')],
                     pname))
+
+
+    def visitTransition(self, t):
+        _YNC = [ ASYNC, SYNC ]
+
+        loc = t.loc
+        impliedDirection, impliedSems = {
+            SEND: [ OUT, _YNC ], RECV: [ IN, _YNC ],
+            CALL: [ OUT, RPC ],  ANSWER: [ IN, RPC ]
+         } [t.trigger]
+        
+        if (OUT is impliedDirection and t.msg.type.isIn()
+            or IN is impliedDirection and t.msg.type.isOut()
+            or _YNC is impliedSems and t.msg.type.isRpc()
+            or RPC is impliedSems and (not t.msg.type.isRpc())):
+            mtype = t.msg.type
+
+            self.errors.append(errormsg(
+                    loc, "%s %s message `%s' is not `%s'd",
+                    mtype.sendSemantics.pretty, mtype.direction.pretty,
+                    t.msg.progname,
+                    trigger))