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.
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.
--- 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))