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.

This commit is contained in:
Chris Jones 2009-07-08 18:03:56 -05:00
parent d3e2234d01
commit 7929ffa13d
11 changed files with 259 additions and 51 deletions

View File

@ -16,9 +16,12 @@ namespace IFrameEmbeddingProtocol {
enum State {
StateStart = 0,
StateError,
StateLast
};
enum IFrameEmbeddingProtocolMsgType {
enum MessageType {
IFrameEmbeddingProtocolStart = IFrameEmbeddingProtocolMsgStart << 12,
IFrameEmbeddingProtocolPreStart = (IFrameEmbeddingProtocolMsgStart << 12) - 1,
Msg_init__ID,
@ -29,6 +32,7 @@ enum IFrameEmbeddingProtocolMsgType {
Reply_move__ID,
IFrameEmbeddingProtocolEnd
};
class Msg_init :
public IPC::Message
{

View File

@ -86,7 +86,7 @@ public:
if (!(IFrameEmbeddingProtocol::Msg_init::Read(&(msg), &(parentWidget)))) {
return MsgPayloadError;
}
if (Answerinit(parentWidget)) {
if (NS_FAILED(Answerinit(parentWidget))) {
return MsgValueError;
}
@ -101,7 +101,7 @@ public:
if (!(IFrameEmbeddingProtocol::Msg_loadURL::Read(&(msg), &(uri)))) {
return MsgPayloadError;
}
if (AnswerloadURL(uri)) {
if (NS_FAILED(AnswerloadURL(uri))) {
return MsgValueError;
}
@ -119,7 +119,7 @@ public:
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;
}

View File

@ -20,9 +20,12 @@ namespace NPAPIProtocol {
enum State {
StateStart = 0,
StateError,
StateLast
};
enum NPAPIProtocolMsgType {
enum MessageType {
NPAPIProtocolStart = NPAPIProtocolMsgStart << 12,
NPAPIProtocolPreStart = (NPAPIProtocolMsgStart << 12) - 1,
Msg_NP_Initialize__ID,
@ -33,6 +36,7 @@ enum NPAPIProtocolMsgType {
Reply_NPPDestructor__ID,
NPAPIProtocolEnd
};
class Msg_NP_Initialize :
public IPC::Message
{

View File

@ -105,7 +105,7 @@ public:
if (!(NPAPIProtocol::Msg_NP_Initialize::Read(&(msg)))) {
return MsgPayloadError;
}
if (AnswerNP_Initialize(&(rv))) {
if (NS_FAILED(AnswerNP_Initialize(&(rv)))) {
return MsgValueError;
}
@ -154,7 +154,7 @@ public:
if (!(__a)) {
return MsgValueError;
}
if (NPPDestructor(__a, &(rv))) {
if (NS_FAILED(NPPDestructor(__a, &(rv)))) {
return MsgValueError;
}
Unregister(__ah.mChildId);

View File

@ -20,9 +20,12 @@ namespace NPPProtocol {
enum State {
StateStart = 0,
StateError,
StateLast
};
enum NPPProtocolMsgType {
enum MessageType {
NPPProtocolStart = NPPProtocolMsgStart << 12,
NPPProtocolPreStart = (NPPProtocolMsgStart << 12) - 1,
Msg_NPP_SetWindow__ID,
@ -33,6 +36,7 @@ enum NPPProtocolMsgType {
Reply_NPN_GetValue__ID,
NPPProtocolEnd
};
class Msg_NPP_SetWindow :
public IPC::Message
{

View File

@ -94,7 +94,7 @@ public:
if (!(NPPProtocol::Msg_NPP_SetWindow::Read(&(msg), &(window)))) {
return MsgPayloadError;
}
if (AnswerNPP_SetWindow(window, &(rv))) {
if (NS_FAILED(AnswerNPP_SetWindow(window, &(rv)))) {
return MsgValueError;
}
@ -110,7 +110,7 @@ public:
if (!(NPPProtocol::Msg_NPP_GetValue::Read(&(msg), &(key)))) {
return MsgPayloadError;
}
if (AnswerNPP_GetValue(key, &(value))) {
if (NS_FAILED(AnswerNPP_GetValue(key, &(value)))) {
return MsgValueError;
}

View File

@ -108,7 +108,7 @@ public:
if (!(NPPProtocol::Msg_NPN_GetValue::Read(&(msg), &(key)))) {
return MsgPayloadError;
}
if (AnswerNPN_GetValue(key, &(value))) {
if (NS_FAILED(AnswerNPN_GetValue(key, &(value)))) {
return MsgValueError;
}

View File

@ -84,6 +84,17 @@ class Visitor:
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
@ -151,31 +162,57 @@ 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
}
@ -235,6 +272,45 @@ class MessageDecl(Node):
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)

View File

@ -97,7 +97,6 @@ class GenerateProtocolHeader(Visitor):
for tdef in self.typedefs:
scope.addstmt(tdef)
def visitTranslationUnit(self, tu):
f = self.file
@ -160,20 +159,30 @@ class GenerateProtocolHeader(Visitor):
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')
@ -184,6 +193,7 @@ class GenerateProtocolHeader(Visitor):
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))
@ -232,6 +242,10 @@ class GenerateProtocolHeader(Visitor):
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') ])
@ -334,12 +348,11 @@ def generateReplyClass(md, 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"
@ -396,7 +409,7 @@ class GenerateProtocolActorHeader(Visitor):
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():

View File

@ -148,7 +148,7 @@ tokens = [
t_COLONCOLON = '::'
literals = '(){}[];,~'
literals = '(){}[];:,~'
t_ignore = ' \f\t\v'
def t_linecomment(t):
@ -175,7 +175,7 @@ def t_STRING(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))
##-----------------------------------------------------------------------------
@ -319,10 +319,49 @@ def p_MessageOutParams(p):
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
@ -394,5 +433,5 @@ def p_QualifiedID(p):
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)

View File

@ -32,7 +32,7 @@
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:
@ -114,6 +114,8 @@ class GeneratedCxxType(CxxType):
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
@ -130,6 +132,10 @@ class IPDLType(Type):
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):
@ -319,11 +325,11 @@ class GatherDecls(Visitor):
# 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)
@ -368,8 +374,7 @@ class GatherDecls(Visitor):
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)
@ -423,6 +428,15 @@ class GatherDecls(Visitor):
"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)
@ -595,6 +609,38 @@ class GatherDecls(Visitor):
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
@ -693,3 +739,25 @@ class CheckTypes(Visitor):
"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))