Import IPDL from cjones' working repo, revision 282b4211d881. New IPDL work will take place in electrolysis.

This commit is contained in:
Benjamin Smedberg 2009-06-30 15:38:59 -04:00
parent cb52b46f78
commit 40fee97f07
12 changed files with 3269 additions and 0 deletions

7
ipc/ipdl/ipdl/Makefile Normal file
View File

@ -0,0 +1,7 @@
.PHONY: clean
clean:
rm -f cxx/*~ *~ *.pyc cxx/*.pyc parser.out ipdl_lextab.py ipdl_yacctab.py
check:
@echo "Implement me! With tests/!"

58
ipc/ipdl/ipdl/__init__.py Normal file
View File

@ -0,0 +1,58 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 *****
__all__ = [ 'gencxx', 'genipdl', 'parse', 'typecheck' ]
import os, sys
from ipdl.cgen import IPDLCodeGen
from ipdl.lower import LowerToCxx
from ipdl.parser import Parser
from ipdl.type import TypeCheck
from ipdl.cxx.cgen import CxxCodeGen
def parse(specstring, filename='<stdin>'):
return Parser().parse(specstring, filename)
def typecheck(ast, errout=sys.stderr):
'''Returns True iff |ast| is well typed. Print errors to |errout| if
it is not.'''
return TypeCheck().check(ast, errout)
def gencxx(ast, outdir):
for hdr in LowerToCxx().lower(ast):
path = os.path.join(outdir, hdr.filename)
CxxCodeGen(outf=open(path, 'w')).cgen(hdr)
def genipdl(ast, outdir):
return IPDLCodeGen().cgen(ast)

280
ipc/ipdl/ipdl/ast.py Normal file
View File

@ -0,0 +1,280 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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
class Visitor:
def defaultVisit(self, node):
raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
node.__class__.__name__)
def visitTranslationUnit(self, tu):
for cxxInc in tu.cxxIncludes:
cxxInc.accept(self)
for protoInc in tu.protocolIncludes:
protoInc.accept(self)
for using in tu.using:
using.accept(self)
tu.protocol.accept(self)
def visitCxxInclude(self, inc):
pass
def visitProtocolInclude(self, inc):
# Note: we don't visit the child AST here, because that needs delicate
# and pass-specific handling
pass
def visitUsingStmt(self, using):
pass
def visitProtocol(self, p):
for namespace in p.namespaces:
namespace.accept(self)
if p.manager is not None:
p.manager.accept(self)
for managed in p.managesStmts:
managed.accept(self)
for msgDecl in p.messageDecls:
msgDecl.accept(self)
for transitionStmt in p.transitionStmts:
transitionStmt.accept(self)
def visitNamespace(self, ns):
pass
def visitManagerStmt(self, mgr):
pass
def visitManagesStmt(self, mgs):
pass
def visitMessageDecl(self, md):
for inParam in md.inParams:
inParam.accept(self)
for outParam in md.outParams:
outParam.accept(self)
def visitParam(self, decl):
pass
def visitTypeSpec(self, ts):
pass
def visitDecl(self, d):
pass
class Loc:
def __init__(self, filename='<??>', lineno=0):
assert filename
self.filename = filename
self.lineno = lineno
def __repr__(self):
return '%r:%r'% (self.filename, self.lineno)
def __str__(self):
return '%s:%s'% (self.filename, self.lineno)
Loc.NONE = Loc(filename='<??>', lineno=0)
class _struct():
pass
class Node:
def __init__(self, loc=Loc.NONE):
self.loc = loc
def accept(self, visitor):
visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
if visit is None:
return getattr(visitor, 'defaultVisit')(self)
return visit(self)
def addAttrs(self, attrsName):
if not hasattr(self, attrsName):
setattr(self, attrsName, _struct())
class TranslationUnit(Node):
def __init__(self):
Node.__init__(self)
self.filename = None
self.cxxIncludes = [ ]
self.protocolIncludes = [ ]
self.using = [ ]
self.protocol = None
def addCxxInclude(self, cxxInclude): self.cxxIncludes.append(cxxInclude)
def addProtocolInclude(self, pInc): self.protocolIncludes.append(pInc)
def addUsingStmt(self, using): self.using.append(using)
def setProtocol(self, protocol): self.protocol = protocol
class CxxInclude(Node):
def __init__(self, loc, cxxFile):
Node.__init__(self, loc)
self.file = cxxFile
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'
class RPC:
pretty = 'Rpc'
class SYNC:
pretty = 'Sync'
class INOUT:
pretty = 'InOut'
class IN:
@staticmethod
def pretty(ss): return _prettyTable['In'][ss.pretty]
class OUT:
@staticmethod
def pretty(ss): return _prettyTable['Out'][ss.pretty]
_prettyTable = {
'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
self.namespaces = [ ]
self.sendSemantics = ASYNC
self.managesStmts = [ ]
self.messageDecls = [ ]
self.transitionStmts = [ ]
def addOuterNamespace(self, namespace):
self.namespaces.insert(0, namespace)
def addManagesStmts(self, managesStmts):
self.managesStmts += managesStmts
def addMessageDecls(self, messageDecls):
self.messageDecls += messageDecls
def addTransitionStmts(self, transStmts):
self.transitionStmts += transStmts
class Namespace(Node):
def __init__(self, loc, namespace):
Node.__init__(self, loc)
self.namespace = namespace
class ManagerStmt(Node):
def __init__(self, loc, managerName):
Node.__init__(self, loc)
self.name = managerName
class ManagesStmt(Node):
def __init__(self, loc, managedName):
Node.__init__(self, loc)
self.name = managedName
class MessageDecl(Node):
def __init__(self, loc):
Node.__init__(self, loc)
self.name = None
self.sendSemantics = ASYNC
self.direction = None
self.inParams = [ ]
self.outParams = [ ]
def addInParams(self, inParamsList):
self.inParams += inParamsList
def addOutParams(self, outParamsList):
self.outParams += outParamsList
def hasReply(self):
return self.sendSemantics is SYNC or self.sendSemantics is RPC
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):
Node.__init__(self, loc)
self.spec = spec
def basename(self):
return self.spec.baseid
def __str__(self): return str(self.spec)
class QualifiedId: # FIXME inherit from node?
def __init__(self, loc, baseid, quals=[ ]):
self.loc = loc
self.baseid = baseid
self.quals = quals
def qualify(self, id):
self.quals.append(self.baseid)
self.baseid = id
def __str__(self):
if 0 == len(self.quals):
return self.baseid
return '::'.join(self.quals) +'::'+ self.baseid
# added by type checking passes
class Decl(Node):
def __init__(self, loc):
Node.__init__(self, loc)
self.progname = None # what the programmer typed, if relevant
self.shortname = None # shortest way to refer to this decl
self.fullname = None # full way to refer to this decl
self.loc = loc
self.type = None
self.scope = None

68
ipc/ipdl/ipdl/builtin.py Normal file
View File

@ -0,0 +1,68 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 *****
# WARNING: the syntax of the builtin types is not checked, so please
# don't add something syntactically invalid. It will not be fun to
# track down the bug.
Types = (
# C types
'char',
'short',
'int',
'long',
'float',
'double',
# stdint types
'int8_t',
'uint8_t',
'int16_t',
'uint16_t',
'int32_t',
'uint32_t',
'int64_t',
'uint64_t',
'intptr_t',
'uintptr_t',
# Mozilla types: "less" standard things we know how serialize/deserialize
'mozilla::ipc::String',
'mozilla::ipc::StringArray',
)
Includes = (
'nscore.h',
'IPC/IPCMessageUtils.h',
'mozilla/ipc/MessageTypes.h',
)

129
ipc/ipdl/ipdl/cgen.py Normal file
View File

@ -0,0 +1,129 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 os, sys
from ipdl.ast import Visitor
from ipdl.ast import IN, OUT, INOUT, ASYNC, SYNC, RPC
class CodePrinter:
def __init__(self, outf=sys.stdout, indentCols=4):
self.outf = outf
self.col = 0
self.indentCols = indentCols
def write(self, str):
self.outf.write(str)
def printdent(self, str=''):
self.write((' '* self.col) + str)
def println(self, str=''):
self.write(str +'\n')
def printdentln(self, str):
self.write((' '* self.col) + str +'\n')
def indent(self): self.col += self.indentCols
def dedent(self): self.col -= self.indentCols
##-----------------------------------------------------------------------------
class IPDLCodeGen(CodePrinter, Visitor):
'''Spits back out equivalent IPDL to the code that generated this.
Also known as pretty-printing.'''
def __init__(self, outf=sys.stdout, indentCols=4, printed=set()):
CodePrinter.__init__(self, outf, indentCols)
self.printed = printed
def visitTranslationUnit(self, tu):
self.printed.add(tu.filename)
self.println('//\n// Automatically generated by ipdlc\n//')
CodeGen.visitTranslationUnit(self, tu)
def visitCxxInclude(self, inc):
self.println('include "'+ inc.file +'";')
def visitProtocolInclude(self, inc):
self.println('include protocol "'+ inc.file +'";')
if inc.tu.filename not in self.printed:
self.println('/* Included file:')
IPDLCodeGen(outf=self.outf, indentCols=self.indentCols,
printed=self.printed).visitTranslationUnit(inc.tu)
self.println('*/')
def visitProtocol(self, p):
self.println()
for namespace in p.namespaces: namespace.accept(self)
self.println('%s protocol %s\n{'% (p.sendSemantics[0], p.name))
self.indent()
for mgs in p.managesStmts:
mgs.accept(self)
if len(p.managesStmts): self.println()
for msgDecl in p.messageDecls: msgDecl.accept(self)
self.println()
for transStmt in p.transitionStmts: transStmt.accept(self)
self.dedent()
self.println('}')
self.write('}\n'* len(p.namespaces))
def visitManagerStmt(self, mgr):
self.printdentln('manager '+ mgr.name +';')
def visitManagesStmt(self, mgs):
self.printdentln('manages '+ mgs.name +';')
def visitMessageDecl(self, msg):
self.printdent('%s %s %s('% (msg.sendSemantics[0], msg.direction[0], msg.name))
for i, inp in enumerate(msg.inParams):
inp.accept(self)
if i != (len(msg.inParams) - 1): self.write(', ')
self.write(')')
if 0 == len(msg.outParams):
self.println(';')
return
self.println()
self.indent()
self.printdent('returns (')
for i, outp in enumerate(msg.outParams):
outp.accept(self)
if i != (len(msg.outParams) - 1): self.write(', ')
self.println(');')
self.dedent()

View File

@ -0,0 +1,34 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 ipdl.cxx.ast
import ipdl.cxx.cgen

426
ipc/ipdl/ipdl/cxx/ast.py Normal file
View File

@ -0,0 +1,426 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 copy, sys
class Visitor:
def defaultVisit(self, node):
raise Exception, "INTERNAL ERROR: no visitor for node type `%s'"% (
node.__class__.__name__)
def visitWhitespace(self, ws):
pass
def visitFile(self, f):
for thing in f.stuff:
thing.accept(self)
def visitCppDirective(self, ppd):
pass
def visitBlock(self, block):
for stmt in block.stmts:
stmt.accept(self)
def visitNamespace(self, ns):
self.visitBlock(ns)
def visitType(self, type):
pass
def visitTypeEnum(self, enum):
pass
def visitTypedef(self, tdef):
tdef.fromtype.accept(self)
tdef.tottype.accept(self)
def visitDecl(self, decl):
decl.type.accept(self)
def visitClass(self, cls):
for viz, parent in cls.inherits:
parent.accept(self)
self.visitBlock(cls)
def visitInherit(self, inh):
pass
def visitMethodDecl(self, meth):
for param in meth.params:
param.accept(self)
if meth.ret is not None:
meth.ret.accept(self)
def visitMethodDefn(self, meth):
meth.decl.accept(self)
self.visitBlock(meth)
def visitConstructorDecl(self, ctor):
self.visitMethodDecl(ctor)
def visitConstructorDefn(self, cd):
cd.decl.accept(self)
for init in cd.memberinits:
init.accept(self)
self.visitBlock(cd)
def visitDestructorDecl(self, dtor):
self.visitMethodDecl(dtor)
def visitDestructorDefn(self, dd):
dd.decl.accept(self)
self.visitBlock(dd)
def visitExprVar(self, v):
pass
def visitExprPrefixUnop(self, e):
e.expr.accept(self)
def visitExprAddrOf(self, eao):
self.visitExprPrefixUnop(eao)
def visitExprDeref(self, ed):
self.visitExprPrefixUnop(ed)
def visitExprSelect(self, es):
es.obj.accept(self)
def visitExprAssn(self, ea):
ea.lhs.accept(self)
ea.rhs.accept(self)
def visitExprCall(self, ec):
ec.func.accept(self)
for arg in ec.args:
arg.accept(self)
def visitExprNew(self, en):
self.visitExprCall(en)
def visitExprDelete(self, ed):
ed.obj.accept(self)
def visitExprMemberInit(self, minit):
self.visitExprCall(minit)
def visitStmtBlock(self, sb):
self.visitBlock(sb)
def visitStmtDecl(self, sd):
sd.decl.accept(self)
def visitLabel(self, label):
pass
def visitCaseLabel(self, case):
pass
def visitDefaultLabel(self, dl):
pass
def visitStmtSwitch(self, ss):
ss.expr.accept(self)
self.visitBlock(ss)
def visitStmtExpr(self, se):
se.expr.accept(self)
def visitStmtReturn(self, sr):
if sr.expr is not None:
sr.expr.accept(self)
##------------------------------
class Node:
def __init__(self):
pass
def accept(self, visitor):
visit = getattr(visitor, 'visit'+ self.__class__.__name__, None)
if visit is None:
return getattr(visitor, 'defaultVisit')(self)
return visit(self)
class Whitespace(Node):
# yes, this is silly. but we need to stick comments in the
# generated code without resorting to more serious hacks
def __init__(self, ws):
Node.__init__(self)
self.ws = ws
Whitespace.NL = Whitespace('\n')
class File(Node):
def __init__(self, filename):
Node.__init__(self)
self.filename = filename
# array of stuff in the file --- stmts and preprocessor thingies
self.stuff = [ ]
def addthing(self, thing):
self.stuff.append(thing)
# "look like" a Block so code doesn't have to care whether they're
# in global scope or not
def addstmt(self, stmt):
self.stuff.append(stmt)
class CppDirective(Node):
'''represents |#[directive] [rest]|, where |rest| is any string'''
def __init__(self, directive, rest):
Node.__init__(self)
self.directive = directive
self.rest = rest
class Block(Node):
def __init__(self):
Node.__init__(self)
self.stmts = [ ]
def addstmt(self, stmt):
self.stmts.append(stmt)
##------------------------------
# type and decl thingies
class Namespace(Block):
def __init__(self, name):
Block.__init__(self)
self.name = name
class Type(Node):
def __init__(self, name, const=False, ptr=False, ref=False):
Node.__init__(self)
self.name = name
self.const = const
self.ptr = ptr
self.ref = ref
# XXX could get serious here with recursive types, but shouldn't
# need that for this codegen
def __deepcopy__(self, memo):
return Type(self.name, self.const, self.ptr, self.ref)
class TypeEnum(Node):
def __init__(self, name=None):
'''name can be None'''
Node.__init__(self)
self.name = name
self.idnums = [ ] # pairs of ('Foo', [num]) or ('Foo', None)
def addId(self, id, num=None):
self.idnums.append((id, num))
class Typedef(Node):
def __init__(self, fromtype, totype):
Node.__init__(self)
self.fromtype = fromtype
self.totype = totype
class Decl(Node):
'''represents |Foo bar|, e.g. in a function signature'''
def __init__(self, type, name):
Node.__init__(self)
self.type = type
self.name = name
def __deepcopy__(self, memo):
return Decl(copy.deepcopy(self.type, memo), self.name)
##------------------------------
# class stuff
class Class(Block):
def __init__(self, name, inherits=[ ],
interface=False, final=False):
assert not (interface and final)
Block.__init__(self)
self.name = name
self.inherits = inherits # array of (viz, Type) pairs
self.interface = interface
self.final = final
class Inherit(Node):
def __init__(self, name, viz='public'):
Node.__init__(self)
self.name = name
self.viz = viz
class MethodDecl(Node):
def __init__(self, name, params=[ ], ret=Type('void'),
virtual=False, const=False, pure=False, static=False):
assert not (virtual and static)
assert not pure or virtual # pure => virtual
Node.__init__(self)
self.name = name
self.params = params
self.ret = ret
self.virtual = virtual
self.const = const
self.pure = pure
self.static = static
def __deepcopy__(self, memo):
return MethodDecl(self.name,
copy.deepcopy(self.params, memo),
copy.deepcopy(self.ret, memo),
self.virtual,
self.const,
self.pure)
class MethodDefn(Block):
def __init__(self, decl):
Block.__init__(self)
self.decl = decl
class ConstructorDecl(MethodDecl):
def __init__(self, name, params=[ ]):
MethodDecl.__init__(self, name, params=params, ret=None)
class ConstructorDefn(MethodDefn):
def __init__(self, decl, memberinits=[ ]):
MethodDefn.__init__(self, decl)
self.memberinits = memberinits
class DestructorDecl(MethodDecl):
def __init__(self, name, virtual=False):
MethodDecl.__init__(self, name, params=[ ], ret=None,
virtual=virtual)
class DestructorDefn(MethodDefn):
def __init__(self, decl): MethodDefn.__init__(self, decl)
##------------------------------
# expressions
class ExprVar(Node):
def __init__(self, name):
Node.__init__(self)
self.name = name
class ExprPrefixUnop(Node):
def __init__(self, expr, op):
self.expr = expr
self.op = op
class ExprAddrOf(ExprPrefixUnop):
def __init__(self, expr):
ExprPrefixUnop.__init__(self, expr, '&')
class ExprDeref(ExprPrefixUnop):
def __init__(self, expr):
ExprPrefixUnop.__init__(self, expr, '*')
class ExprSelect(Node):
def __init__(self, obj, op, field):
Node.__init__(self)
self.obj = obj
self.op = op
self.field = field
class ExprAssn(Node):
def __init__(self, lhs, rhs):
Node.__init__(self)
self.lhs = lhs
self.rhs = rhs
class ExprCall(Node):
def __init__(self, func, args=[ ]):
Node.__init__(self)
self.func = func
self.args = args
class ExprNew(ExprCall):
# XXX taking some poetic license ...
def __init__(self, type, args=[ ]):
ExprCall.__init__(self, ExprVar(type.name), args)
class ExprDelete(Node):
def __init__(self, obj):
Node.__init__(self)
self.obj = obj
class ExprMemberInit(ExprCall):
def __init__(self, member, args=[ ]):
ExprCall.__init__(self, member, args)
##------------------------------
# statements etc.
class StmtBlock(Block):
def __init__(self):
Block.__init__(self)
class StmtDecl(Node):
def __init__(self, decl):
Node.__init__(self)
self.decl = decl
class Label(Node):
def __init__(self, name):
Node.__init__(self)
self.name = name
class CaseLabel(Node):
def __init__(self, name):
Node.__init__(self)
self.name = name
class DefaultLabel(Node):
def __init__(self):
Node.__init__(self)
class StmtIf(Node):
def __init__(self, cond):
Node.__init__(self)
self.cond = cond
self.ifb = Block()
self.elseb = None
def addifstmt(self, stmt):
self.ifb.addstmt(stmt)
def addelsestmt(self, stmt):
if self.elseb is None: self.elseb = Block()
self.elseb.addstmt(stmt)
class StmtSwitch(Block):
def __init__(self, expr):
Block.__init__(self)
self.expr = expr
def addcase(self, case, block):
'''NOTE: |case| is not checked for uniqueness'''
self.addstmt(case)
self.addstmt(block)
class StmtExpr(Node):
def __init__(self, expr):
Node.__init__(self)
self.expr = expr
class StmtReturn(Node):
def __init__(self, expr=None):
Node.__init__(self)
self.expr = expr

337
ipc/ipdl/ipdl/cxx/cgen.py Normal file
View File

@ -0,0 +1,337 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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.cgen import CodePrinter
from ipdl.cxx.ast import Visitor
class CxxCodeGen(CodePrinter, Visitor):
def __init__(self, outf=sys.stdout, indentCols=4):
CodePrinter.__init__(self, outf, indentCols)
def cgen(self, cxxfile):
cxxfile.accept(self)
def visitWhitespace(self, ws):
self.write(ws.ws)
def visitCppDirective(self, cd):
self.println('#%s %s'% (cd.directive, cd.rest))
def visitNamespace(self, ns):
self.println('namespace '+ ns.name +' {')
self.visitBlock(ns)
self.println('} // namespace '+ ns.name)
def visitType(self, t):
ts = ''
if t.const: ts += 'const '
ts += t.name
if t.ptr: ts += '*'
if t.ref: ts += '&'
self.write(ts)
def visitTypeEnum(self, te):
self.write('enum')
if te.name:
self.write(' '+ te.name)
self.println(' {')
self.indent()
nids = len(te.idnums)
for i, (id, num) in enumerate(te.idnums):
self.printdent(id)
if num:
self.write(' = '+ str(num))
if i != (nids-1):
self.write(',')
self.println()
self.dedent()
self.printdent('}')
def visitTypedef(self, td):
self.printdent('typedef ')
td.fromtype.accept(self)
self.write(' ')
td.totype.accept(self)
self.println(';')
def visitDecl(self, d):
d.type.accept(self)
if d.name:
self.write(' '+ d.name)
def visitClass(self, c):
self.printdent('class')
if c.interface:
# FIXME/cjones: turn this "on" when we get the analysis
self.write(' /*NS_INTERFACE_CLASS*/')
if c.final:
self.write(' /*NS_FINAL_CLASS*/')
self.write(' '+ c.name)
ninh = len(c.inherits)
if 0 < ninh:
self.println(' :')
self.indent()
for i, inherit in enumerate(c.inherits):
self.printdent()
inherit.accept(self)
if i != (ninh - 1):
self.println(',')
self.dedent()
self.println()
self.printdentln('{')
self.indent()
self.visitBlock(c)
self.dedent()
self.printdentln('};')
def visitInherit(self, inh):
self.write(inh.viz +' '+ inh.name)
def visitMethodDecl(self, md):
assert not (md.static and md.virtual)
if md.static:
self.write('static ')
if md.virtual:
self.write('virtual ')
if md.ret:
md.ret.accept(self)
self.write(' ')
self.write(md.name +'(')
self.writeDeclList(md.params)
self.write(')')
if md.const:
self.write(' const')
if md.pure:
self.write(' = 0')
def visitMethodDefn(self, md):
self.printdent()
md.decl.accept(self)
self.println()
self.printdentln('{')
self.indent()
self.visitBlock(md)
self.dedent()
self.printdentln('}')
def visitConstructorDecl(self, cd):
# FIXME/cjones: explicit when possible
self.visitMethodDecl(cd)
def visitConstructorDefn(self, cd):
self.printdent()
cd.decl.accept(self)
if len(cd.memberinits):
self.println(' :')
self.indent()
ninits = len(cd.memberinits)
for i, init in enumerate(cd.memberinits):
self.printdent()
init.accept(self)
if i != (ninits-1):
self.println(',')
self.dedent()
self.println()
self.printdentln('{')
self.indent()
self.visitBlock(cd)
self.dedent()
self.printdentln('}')
def visitDestructorDecl(self, dd):
if dd.virtual:
self.write('virtual ')
self.write('~'+ dd.name +'()')
def visitDestructorDefn(self, dd):
self.printdent()
dd.decl.accept(self)
self.println()
self.printdentln('{')
self.indent()
self.visitBlock(dd)
self.dedent()
self.printdentln('}')
def visitExprVar(self, ev):
self.write(ev.name)
def visitExprPrefixUnop(self, e):
self.write(e.op)
self.write('(')
e.expr.accept(self)
self.write(')')
def visitExprSelect(self, es):
es.obj.accept(self)
self.write(es.op + es.field)
def visitExprAssn(self, ea):
ea.lhs.accept(self)
self.write(' = ')
ea.rhs.accept(self)
def visitExprCall(self, ec):
ec.func.accept(self)
self.write('(')
self.writeExprList(ec.args)
self.write(')')
def visitExprNew(self, en):
self.write('new ')
self.visitExprCall(en)
def visitExprDelete(self, ed):
self.write('delete ')
ed.accept(self)
def visitStmtBlock(self, b):
self.printdentln('{')
self.indent()
self.visitBlock(b)
self.dedent()
self.printdentln('}')
def visitLabel(self, label):
self.dedent() # better not be at global scope ...
self.printdentln(label.name +':')
self.indent()
def visitCaseLabel(self, cl):
self.dedent()
self.printdentln('case '+ cl.name +':')
self.indent()
def visitDefaultLabel(self, dl):
self.dedent()
self.printdentln('default:')
self.indent()
def visitStmtIf(self, si):
self.printdent('if (')
si.cond.accept(self)
self.println(') {')
self.indent()
si.ifb.accept(self)
self.dedent()
self.printdentln('}')
if si.elseb is not None:
self.printdentln('else {')
self.indent()
si.elseb.accept(self)
self.dedent()
self.println('}')
def visitStmtSwitch(self, sw):
self.printdent('switch (')
sw.expr.accept(self)
self.println(') {')
self.indent()
self.visitBlock(sw)
self.dedent()
self.printdentln('}')
def visitStmtDecl(self, sd):
self.printdent()
sd.decl.accept(self)
self.println(';')
def visitStmtExpr(self, se):
self.printdent()
se.expr.accept(self)
self.println(';')
def visitStmtReturn(self, sr):
self.printdent('return')
if sr.expr:
self.write (' ')
sr.expr.accept(self)
self.println(';')
def writeDeclList(self, decls):
# FIXME/cjones: try to do nice formatting of these guys
ndecls = len(decls)
if 0 == ndecls:
return
elif 1 == ndecls:
decls[0].accept(self)
return
self.indent()
self.indent()
self.indent()
for i, decl in enumerate(decls):
self.println()
self.printdent()
decl.accept(self)
if i != (ndecls-1):
self.write(',')
self.dedent()
self.dedent()
self.dedent()
def writeExprList(self, exprs):
# FIXME/cjones: try to do nice formatting and share code with
# writeDeclList()
nexprs = len(exprs)
for i, expr in enumerate(exprs):
expr.accept(self)
if i != (nexprs-1):
self.write(', ')

729
ipc/ipdl/ipdl/lower.py Normal file
View File

@ -0,0 +1,729 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 os
from copy import deepcopy
from ipdl.ast import Visitor, ASYNC, SYNC, RPC, IN, OUT, INOUT
import ipdl.cxx.ast as cxx
def _joinProtocolNamespacesName(sep, p, pname):
return sep.join([ ns.namespace for ns in p.namespaces ] + [ pname ])
def _protocolIncludeGuard(p, pname):
return _joinProtocolNamespacesName('_', p, pname) +'_h'
def _protocolHeaderFilename(p, pname):
return _joinProtocolNamespacesName(os.path.sep, p, pname)+ '.h'
def _protocolHeaderName(pname):
return pname +'Protocol'
class _struct: pass
class LowerToCxx:
def lower(self, tu):
'''returns a list of cxx.File representing the lowered form of |tu|'''
pname = _protocolHeaderName(tu.protocol.name)
pheader = cxx.File(pname +'.h')
GenerateProtocolHeader().lower(tu, pname, pheader)
parentname = pname +'Parent'
parentheader = cxx.File(parentname +'.h')
GenerateProtocolParentHeader().lower(
tu, pname, parentname, parentheader)
childname = pname +'Child'
childheader = cxx.File(childname +'.h')
GenerateProtocolChildHeader().lower(
tu, pname, childname, childheader)
return pheader, parentheader, childheader
##-----------------------------------------------------------------------------
class GenerateProtocolHeader(Visitor):
'''creates a "generic" protocol header from an IPDL AST'''
def __init__(self):
self.pname = None
self.file = None
self.ns = None # the namespace we toss all this stuff into
# list of typedefs for the protocol namespace. these are
# produced from various places in the AST and collected here
self.typedefs = [ ]
def lower(self, tu, protocol, outcxxfile):
self.pname = protocol
self.file = outcxxfile
tu.accept(self)
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
//
'''))
includeguard = _protocolIncludeGuard(tu.protocol, self.pname)
f.addthing(cxx.CppDirective('ifndef', includeguard))
f.addthing(cxx.CppDirective('define', includeguard))
f.addthing(cxx.Whitespace.NL)
Visitor.visitTranslationUnit(self, tu)
f.addthing(cxx.Whitespace.NL)
f.addthing(cxx.CppDirective('endif', '// ifndef '+ includeguard))
def visitCxxInclude(self, inc):
self.file.addthing(cxx.CppDirective('include', '"'+ inc.file +'"'))
def visitProtocolInclude(self, inc):
p = inc.tu.protocol
self.file.addthing(
cxx.CppDirective(
'include',
'"'+ _protocolHeaderFilename(p, _protocolHeaderName(p.name)) +'"'))
# FIXME/cjones: not clear what types we need from other header
# if p.decl.fullname is not None:
# self.typedef(p.decl.fullname, p.decl.shortname)
def visitUsingStmt(self, using):
if using.decl.fullname is not None:
self.typedef(using.decl.fullname, using.decl.shortname)
def visitProtocol(self, p):
self.file.addthing(cxx.Whitespace.NL)
# construct the namespace into which we'll stick all our decls
if 0 == len(p.namespaces):
self.ns = self.file
else:
innernamespaces = p.namespaces[1:]
self.ns = cxx.Namespace(p.namespaces[0].namespace)
self.file.addthing(self.ns)
for ns in innernamespaces:
innerns = cxx.Namespace(ns.namespace)
self.ns.addstmt(innerns)
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)
# previsit the messages and stash away some common info used
# several times later
for md in p.messageDecls:
md.accept(self)
# generate parent and child interfaces
iparent = cxx.Class('IParent', interface=True)
iparent.addstmt(cxx.Label('protected'))
self.injectTypedefs(iparent)
iparent.addstmt(cxx.Whitespace.NL)
p.accept(GenerateParentInterface(iparent))
ns.addstmt(iparent)
ns.addstmt(cxx.Whitespace.NL)
ns.addstmt(cxx.Whitespace.NL)
ichild = cxx.Class('IChild', interface=True)
ichild.addstmt(cxx.Label('protected'))
self.injectTypedefs(ichild)
ichild.addstmt(cxx.Whitespace.NL)
p.accept(GenerateChildInterface(ichild))
ns.addstmt(ichild)
ns.addstmt(cxx.Whitespace.NL)
# 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.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, '')))
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)
def visitMessageDecl(self, md):
# where we squirrel away some common information
setattr(md, '_cxx', _struct())
md._cxx.params = [ ]
for param in md.inParams:
md._cxx.params.append(cxx.Decl(cxx.Type(param.type.name()),
param.progname))
md._cxx.returns = [ ]
for param in md.outParams:
md._cxx.returns.append(cxx.Decl(cxx.Type(param.type.name()),
param.progname))
# generate C++ interface to message sending/handling
method = cxx.MethodDecl(
name=md.decl.progname,
params=[ ],
ret=cxx.Type('nsresult'),
virtual=True)
for param in md._cxx.params:
pcopy = deepcopy(param)
pcopy.type.const = True
pcopy.type.ref = True
method.params.append(pcopy)
for ret in md._cxx.returns:
rcopy = deepcopy(ret)
rcopy.type.ptr = True
method.params.append(rcopy)
md._cxx.method = method
# 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)
class GenerateInterface(Visitor):
def __init__(self, iface, name):
self.iface = iface
self.name = name
def visitProtocol(self, p):
ifc = self.iface
n = self.name
ifc.addstmt(cxx.Label('public'))
nmsgs = len(p.messageDecls)
for i, msgdecl in enumerate(p.messageDecls):
msgdecl.accept(self)
ifc.addstmt(cxx.Whitespace.NL)
ifc.addstmt(cxx.Label('protected'))
ifc.addstmt(cxx.ConstructorDefn(
cxx.ConstructorDecl(n)))
ifc.addstmt(cxx.DestructorDefn(
cxx.DestructorDecl(n, virtual=True)))
# disable unwanted ctors/operators
ifc.addstmt(cxx.Whitespace.NL)
ifc.addstmt(cxx.Label('private'))
ref = cxx.Type(n, ref=True)
constref = cxx.Type(n, const=True, ref=True)
ifc.addstmt(cxx.StmtDecl(
cxx.ConstructorDecl(n,
params=[ cxx.Decl(constref, '')])))
ifc.addstmt(cxx.StmtDecl(
cxx.MethodDecl('operator=',
params= [ cxx.Decl(constref, '') ],
ret=ref)))
def visitMessageDecl(self, md):
if self.msgComesIn(md.decl.type):
method = deepcopy(md._cxx.method)
method.pure = True
self.iface.addstmt(cxx.StmtDecl(method))
self.iface.addstmt(cxx.Whitespace.NL)
class GenerateParentInterface(GenerateInterface):
def __init__(self, iparent):
GenerateInterface.__init__(self, iparent, iparent.name)
def msgComesIn(self, mtype): return mtype.isIn() or mtype.isInout()
class GenerateChildInterface(GenerateInterface):
def __init__(self, iparent):
GenerateInterface.__init__(self, iparent, iparent.name)
def msgComesIn(self, mtype): return mtype.isOut() or mtype.isInout()
def generateMsgClass(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'))
idenum = cxx.TypeEnum()
idenum.addId('ID', clsname +'__ID')
cls.addstmt(cxx.StmtDecl(cxx.Decl(idenum, '')))
# FIXME/cjones: need to handle "managed" messages
constparams = deepcopy(params)
writestmts = [ ]
for cparam in constparams:
cparam.type.const = True
cparam.type.ref = True
writestmts.append(
cxx.StmtExpr(cxx.ExprCall(cxx.ExprVar('IPC::WriteParam'),
[ cxx.ExprVar('this'),
cxx.ExprVar(cparam.name) ])))
# make the message constructor (serializer)
ctordecl = cxx.ConstructorDecl(clsname, params=constparams)
superinit = cxx.ExprMemberInit(
cxx.ExprVar('IPC::Message'),
[ cxx.ExprVar('MSG_ROUTING_CONTROL'),
cxx.ExprVar('ID'),
cxx.ExprVar('PRIORITY_NORMAL') ])
ctor = cxx.ConstructorDefn(ctordecl, [ superinit ])
for writestmt in writestmts:
ctor.addstmt(writestmt)
cls.addstmt(ctor)
cls.addstmt(cxx.Whitespace.NL)
# make the message deserializer
outparams = deepcopy(params)
for oparam in outparams:
oparam.type.ptr = True
reader = cxx.MethodDefn(
cxx.MethodDecl(
'Read',
params=([ cxx.Decl(cxx.Type('Message', ptr=True, const=True),
'msg') ]
+ outparams),
ret=cxx.Type('bool'),
static=True))
# avoid generating an unused variable when we don't deserialize
# anything. why generate the method anyway? it keeps other code
# consistent, and we might do some checking in here eventually
if len(outparams):
# hack
reader.addstmt(
cxx.StmtDecl(cxx.Decl(cxx.Type('void', ptr=True), 'iter = 0')))
reader.addstmt(cxx.Whitespace.NL)
for oparam in outparams:
cond = cxx.ExprPrefixUnop(
cxx.ExprCall(cxx.ExprVar('IPC::ReadParam'),
[ cxx.ExprVar('msg'),
cxx.ExprAddrOf(cxx.ExprVar('iter')),
cxx.ExprVar(oparam.name) ]),
'!')
ifstmt = cxx.StmtIf(cond)
# false isn't a var
ifstmt.addifstmt(cxx.StmtReturn(cxx.ExprVar('false')))
reader.addstmt(ifstmt)
reader.addstmt(cxx.Whitespace.NL)
# false isn't a var
reader.addstmt(cxx.StmtReturn(cxx.ExprVar('true')))
cls.addstmt(reader)
return cls
def generateMessageClass(md, typedefInjector):
return generateMsgClass(md._cxx.id, md._cxx.params, typedefInjector)
def generateReplyClass(md, typedefInjector):
return generateMsgClass(md._cxx.replyid, md._cxx.returns, typedefInjector)
##-----------------------------------------------------------------------------
_channelTable = {
'Async': [ 'mozilla', 'ipc', 'AsyncChannel' ],
'Sync': [ 'mozilla', 'ipc', 'SyncChannel' ],
'Rpc': [ 'mozilla', 'ipc', 'RPCChannel' ]
}
class GenerateProtocolActorHeader(Visitor):
def __init__(self, thisiface, thatiface):
self.thisiface = thisiface
self.thatiface = thatiface
self.clsname = None
self.pname = None
self.file = None
self.ns = None
def lower(self, tu, pname, clsname, cxxHeaderFile):
self.pname = pname
self.clsname = clsname
self.file = cxxHeaderFile
tu.accept(self)
def visitTranslationUnit(self, tu):
f = self.file
f.addthing(cxx.Whitespace('''//
// Automatically generated by ipdlc.
// Edit at your own risk
//
'''))
includeguard = _protocolIncludeGuard(tu.protocol, self.clsname)
f.addthing(cxx.CppDirective('ifndef', includeguard))
f.addthing(cxx.CppDirective('define', includeguard))
f.addthing(cxx.Whitespace.NL)
mainheader = _protocolHeaderFilename(tu.protocol, self.pname)
f.addthing(cxx.CppDirective('include', '"'+ mainheader +'"'))
tu.protocol.accept(self)
f.addthing(cxx.Whitespace.NL)
f.addthing(cxx.CppDirective('endif', '// ifndef '+ includeguard))
def visitProtocol(self, p):
channel = _channelTable[p.decl.type.sendSemantics.pretty]
channelname = '::'.join(channel)
channelfile = '/'.join(channel) +'.h'
if p.decl.type.isToplevel():
self.channelsel = '.'
else:
self.channelsel = '->'
self.file.addthing(cxx.CppDirective('include', '"'+ channelfile +'"'))
self.file.addthing(cxx.Whitespace.NL)
# construct the namespace into which we'll stick all our decls
if 0 == len(p.namespaces):
self.ns = self.file
else:
innernamespaces = p.namespaces[1:]
self.ns = cxx.Namespace(p.namespaces[0].namespace)
self.file.addthing(self.ns)
for ns in innernamespaces:
innerns = cxx.Namespace(ns.namespace)
self.ns.addstmt(innerns)
self.ns = innerns
self.ns.addstmt(cxx.Whitespace.NL)
self.ns.addstmt(cxx.Whitespace.NL)
iface = p.decl.fullname +'Protocol::'+ self.thatiface
cls = cxx.Class(self.clsname,
inherits=[ cxx.Inherit(iface),
cxx.Inherit(channelname +'::Listener') ],
final=True)
cls.addstmt(cxx.Label('private'))
impliface = p.decl.fullname +'Protocol::'+ self.thisiface
cls.addstmt(cxx.Typedef(cxx.Type('IPC::Message'),
cxx.Type('Message')))
cls.addstmt(cxx.Typedef(cxx.Type(channelname),
cxx.Type('Channel')))
cls.addstmt(cxx.Typedef(cxx.Type(impliface),
cxx.Type(self.thisiface)))
cls.addstmt(cxx.Whitespace.NL)
# TODO manager param to constructor, when protocol is managed
cls.addstmt(cxx.Label('public'))
ctor = cxx.ConstructorDefn(
cxx.ConstructorDecl(
self.clsname,
[ cxx.Decl(cxx.Type(self.thisiface, ptr=True), 'aImpl') ]),
[ cxx.ExprMemberInit(cxx.ExprVar('mImpl'),
[ cxx.ExprVar('aImpl') ]) ])
if p.decl.type.isToplevel():
ctor.memberinits.append(
cxx.ExprMemberInit(cxx.ExprVar('mChannel'),
[ cxx.ExprVar('this') ]))
cls.addstmt(ctor)
cls.addstmt(cxx.Whitespace.NL)
dtor = cxx.DestructorDefn(
cxx.DestructorDecl(self.clsname, virtual=True))
cls.addstmt(dtor)
cls.addstmt(cxx.Whitespace.NL)
if p.decl.type.isToplevel():
# open
openmeth = cxx.MethodDefn(
cxx.MethodDecl(
'Open',
params=[ cxx.Decl(cxx.Type('IPC::Channel', ptr=True),
'aChannel'),
cxx.Decl(cxx.Type('MessageLoop', ptr=True),
'aThread = 0') ],
ret=cxx.Type('bool')))
openmeth.addstmt(cxx.StmtReturn(
cxx.ExprCall(
cxx.ExprSelect(cxx.ExprVar('mChannel'), '.', 'Open'),
[ cxx.ExprVar('aChannel'), cxx.ExprVar('aThread') ])))
cls.addstmt(openmeth)
cls.addstmt(cxx.Whitespace.NL)
# close
closemeth = cxx.MethodDefn(cxx.MethodDecl('Close'))
closemeth.addstmt(cxx.StmtExpr(
cxx.ExprCall(
cxx.ExprSelect(cxx.ExprVar('mChannel'), '.', 'Close'))))
cls.addstmt(closemeth)
cls.addstmt(cxx.Whitespace.NL)
# incoming message dispatchers
self.asyncswitch = cxx.StmtSwitch(
cxx.ExprCall(cxx.ExprSelect(cxx.ExprVar('msg'), '.', 'type'), [ ]))
if p.decl.type.talksSync():
self.syncswitch = deepcopy(self.asyncswitch)
if p.decl.type.talksRpc():
self.rpcswitch = deepcopy(self.syncswitch)
# implement child iface and add handlers to message switches
self.cls = cls
for md in p.messageDecls:
self.visitMessageDecl(md)
# add default cases
default = cxx.StmtBlock()
default.addstmt(cxx.StmtReturn(cxx.ExprVar('MsgNotKnown')))
self.asyncswitch.addcase(cxx.DefaultLabel(), default)
if p.decl.type.talksSync():
self.syncswitch.addcase(cxx.DefaultLabel(), default)
if p.decl.type.talksRpc():
self.rpcswitch.addcase(cxx.DefaultLabel(), default)
asynchandler = cxx.MethodDefn(
cxx.MethodDecl(
'OnMessageReceived', virtual=True,
params=[ cxx.Decl(cxx.Type('Message', const=1, ref=1),'msg') ],
ret=cxx.Type('Result')))
if p.decl.type.talksSync():
synchandler = deepcopy(asynchandler)
synchandler.decl.params.append(cxx.Decl(
cxx.Type('Message', ref=1, ptr=1), 'reply'))
if p.decl.type.talksRpc():
rpchandler = deepcopy(synchandler)
rpchandler.decl.name = 'OnCallReceived'
asynchandler.addstmt(self.asyncswitch)
cls.addstmt(asynchandler)
cls.addstmt(cxx.Whitespace.NL)
if p.decl.type.talksSync():
synchandler.addstmt(self.syncswitch)
cls.addstmt(synchandler)
cls.addstmt(cxx.Whitespace.NL)
if p.decl.type.talksRpc():
rpchandler.addstmt(self.rpcswitch)
cls.addstmt(rpchandler)
cls.addstmt(cxx.Whitespace.NL)
# private members and methods
# TODO handle manager stuff: lookups, routing
cls.addstmt(cxx.Label('private'))
cls.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type(self.thisiface, ptr=True),
'mImpl')))
channeltype = cxx.Type('Channel')
if p.decl.type.isManaged():
channeltype.ptr = True # subprotocols inherit this
cls.addstmt(cxx.StmtDecl(cxx.Decl(channeltype, 'mChannel')))
self.ns.addstmt(cls)
self.ns.addstmt(cxx.Whitespace.NL)
self.ns.addstmt(cxx.Whitespace.NL)
def visitMessageDecl(self, md):
# TODO special handling of constructor messages
# create method for "that" interface
if self.sendsMessage(md):
impl = cxx.MethodDefn(md._cxx.method)
impl.addstmt(cxx.StmtDecl(cxx.Decl(cxx.Type('nsresult'), '__rv')))
rv = cxx.ExprVar('__rv')
failif = cxx.StmtIf(rv)
failif.ifb.addstmt(cxx.StmtReturn(rv))
hasreply = md.decl.type.hasReply()
if hasreply:
impl.addstmt(cxx.StmtDecl(
cxx.Decl(cxx.Type('Message'), 'reply')))
reply = cxx.ExprVar('reply')
impl.addstmt(cxx.Whitespace.NL)
sendcall = cxx.ExprCall(
cxx.ExprSelect(
cxx.ExprVar('mChannel'), self.channelsel, 'Call'),
[ cxx.ExprNew(cxx.Type(md._cxx.nsid),
[ cxx.ExprVar(p.name)
for p in md._cxx.params ]) ])
if hasreply:
sendcall.args.append(cxx.ExprAddrOf(reply))
# TODO special handling of actor handles
impl.addstmt(cxx.StmtExpr(cxx.ExprAssn(rv, sendcall)))
if not hasreply:
impl.addstmt(cxx.StmtReturn(rv))
self.cls.addstmt(impl)
self.cls.addstmt(cxx.Whitespace.NL)
else:
impl.addstmt(failif)
unpack = cxx.ExprCall(cxx.ExprVar(md._cxx.nsreplyid +'::Read'),
[ cxx.ExprAddrOf(cxx.ExprVar('reply')) ]
+ [ cxx.ExprVar(r.name)
for r in md._cxx.returns ])
errhandle = cxx.StmtIf(cxx.ExprPrefixUnop(unpack, '!'))
errhandle.ifb.addstmt(cxx.StmtReturn(
cxx.ExprVar('MsgPayloadError')))
impl.addstmt(errhandle)
# TODO special handling of actor handles
impl.addstmt(cxx.StmtReturn(cxx.ExprVar('NS_OK')))
self.cls.addstmt(impl)
self.cls.addstmt(cxx.Whitespace.NL)
# create case for this message in the big handler switch statement
if self.receivesMessage(md):
case = cxx.CaseLabel(md._cxx.nsid +'__ID')
block = cxx.StmtBlock()
rv = cxx.ExprVar('__rv')
for param in md._cxx.params:
block.addstmt(cxx.StmtDecl(param))
for ret in md._cxx.returns:
block.addstmt(cxx.StmtDecl(ret))
block.addstmt(cxx.Whitespace.NL)
unpack = cxx.ExprCall(cxx.ExprVar(md._cxx.nsid +'::Read'),
[ cxx.ExprAddrOf(cxx.ExprVar('msg')) ]
+ [ cxx.ExprAddrOf(cxx.ExprVar(p.name))
for p in md._cxx.params ])
errhandle = cxx.StmtIf(cxx.ExprPrefixUnop(unpack, '!'))
errhandle.ifb.addstmt(cxx.StmtReturn(
cxx.ExprVar('MsgPayloadError')))
block.addstmt(errhandle)
# TODO special handling of actor handles
callimpl = cxx.ExprCall(
cxx.ExprSelect(cxx.ExprVar('mImpl'), '->',
md.decl.progname), [ ])
callimpl.args += [ cxx.ExprVar(p.name) for p in md._cxx.params ]
callimpl.args += [ cxx.ExprAddrOf(cxx.ExprVar(r.name))
for r in md._cxx.returns ]
errhandle = cxx.StmtIf(callimpl)
errhandle.ifb.addstmt(cxx.StmtReturn(
cxx.ExprVar('MsgValueError')))
block.addstmt(errhandle)
# TODO special handling of actor handles
if md.decl.type.hasReply():
replymsg = cxx.ExprNew(
cxx.Type(md._cxx.nsreplyid),
[ cxx.ExprVar(r.name) for r in md._cxx.returns ])
block.addstmt(cxx.StmtExpr(cxx.ExprAssn(cxx.ExprVar('reply'),
replymsg)))
block.addstmt(cxx.StmtReturn(cxx.ExprVar('MsgProcessed')))
if md.decl.type.isAsync():
self.asyncswitch.addcase(case, block)
elif md.decl.type.isSync():
self.syncswitch.addcase(case, block)
else:
self.rpcswitch.addcase(case, block)
class GenerateProtocolParentHeader(GenerateProtocolActorHeader):
def __init__(self):
GenerateProtocolActorHeader.__init__(self, 'IParent', 'IChild')
def sendsMessage(self, md):
return not md.decl.type.isIn()
def receivesMessage(self, md):
return md.decl.type.isInout() or md.decl.type.isIn()
class GenerateProtocolChildHeader(GenerateProtocolActorHeader):
def __init__(self):
GenerateProtocolActorHeader.__init__(self, 'IChild', 'IParent')
def sendsMessage(self, md):
return not md.decl.type.isOut()
def receivesMessage(self, md):
return md.decl.type.isInout() or md.decl.type.isOut()

381
ipc/ipdl/ipdl/parser.py Normal file
View File

@ -0,0 +1,381 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 os, sys
from ply import lex, yacc
from ipdl.ast import *
def _getcallerpath():
'''Return the absolute path of the file containing the code that
**CALLED** this function.'''
return os.path.abspath(sys._getframe(1).f_code.co_filename)
# we want PLY to generate its output in the module directory, not wherever
# the user chooses to run ipdlc from
_thisdir, _ = os.path.split(_getcallerpath())
##-----------------------------------------------------------------------------
class Parser:
# when we reach an |include protocol "foo.ipdl";| statement, we need to
# save the current parser state and create a new one. this "stack" is
# where that state is saved
#
# there is one Parser per file
current = None
parseStack = [ ]
parsed = { }
def __init__(self, debug=0):
self.debug = debug
self.filename = None
self.loc = None # not always up to date
self.lexer = None
self.parser = None
self.tu = TranslationUnit()
def parse(self, input, filename):
realpath = os.path.abspath(filename)
if realpath in Parser.parsed:
return Parser.parsed[realpath].tu
self.lexer = lex.lex(debug=self.debug,
optimize=not self.debug,
lextab="ipdl_lextab",
outputdir=_thisdir)
self.parser = yacc.yacc(debug=self.debug,
optimize=not self.debug,
tabmodule="ipdl_yacctab",
outputdir=_thisdir)
self.filename = filename
self.tu.filename = realpath
Parser.parsed[realpath] = self
Parser.parseStack.append(Parser.current)
Parser.current = self
ast = self.parser.parse(input=input, lexer=self.lexer,
debug=self.debug)
Parser.current = Parser.parseStack.pop()
return ast
# returns a GCC-style string representation of the include stack.
# e.g.,
# in file included from 'foo.ipdl', line 120:
# in file included from 'bar.ipd', line 12:
# which can be printed above a proper error message or warning
@staticmethod
def includeStackString():
s = ''
for parse in Parser.parseStack[1:]:
s += " in file included from `%s', line %d:\n"% (
parse.loc.filename, parse.loc.lineno)
return s
def locFromTok(p, num):
return Loc(Parser.current.filename, p.lineno(num))
##-----------------------------------------------------------------------------
reserved = set((
'answer',
'async',
'call',
'goto',
'in',
'include',
'inout',
'manager',
'manages',
'namespace',
'out',
'protocol',
'recv',
'returns',
'rpc',
'send',
'share',
'sync',
'transfer',
'using'))
tokens = [
'COLONCOLON', 'ID', 'STRING'
] + [ r.upper() for r in reserved ]
t_COLONCOLON = '::'
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')
def t_NL(t):
r'\n+'
t.lexer.lineno += len(t.value)
def t_ID(t):
r'[a-zA-Z_][a-zA-Z0-9_]*'
if t.value in reserved:
t.type = t.value.upper()
return 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'% (
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]:
if isinstance(stmt, CxxInclude): tu.addCxxInclude(stmt)
elif isinstance(stmt, ProtocolInclude): tu.addProtocolInclude(stmt)
elif isinstance(stmt, UsingStmt): tu.addUsingStmt(stmt)
else:
assert 0
tu.protocol = p[2]
p[0] = tu
##--------------------
## Preamble
def p_Preamble(p):
"""Preamble : Preamble PreambleStmt ';'
|"""
if 1 == len(p):
p[0] = [ ]
else:
p[1].append(p[2])
p[0] = p[1]
def p_PreambleStmt(p):
"""PreambleStmt : CxxIncludeStmt
| ProtocolIncludeStmt
| UsingStmt"""
p[0] = p[1]
def p_CxxIncludeStmt(p):
"""CxxIncludeStmt : INCLUDE STRING"""
p[0] = CxxInclude(locFromTok(p, 1), p[2])
def p_ProtocolIncludeStmt(p):
"""ProtocolIncludeStmt : INCLUDE PROTOCOL STRING"""
loc = locFromTok(p, 1)
Parser.current.loc = loc
inc = ProtocolInclude(loc, p[3])
inc.tu = Parser().parse(open(inc.file).read(), inc.file)
p[0] = inc
def p_UsingStmt(p):
"""UsingStmt : USING CxxType"""
p[0] = UsingStmt(locFromTok(p, 1), p[2])
##--------------------
## Protocol definition
def p_NamespacedProtocolDefn(p):
"""NamespacedProtocolDefn : NAMESPACE ID '{' NamespacedProtocolDefn '}'
| ProtocolDefn"""
if 2 == len(p):
p[0] = p[1]
else:
protocol = p[4]
protocol.addOuterNamespace(Namespace(locFromTok(p, 1), p[2]))
p[0] = protocol
def p_ProtocolDefn(p):
"""ProtocolDefn : SendSemanticsQual PROTOCOL ID '{' ManagerStmtOpt ManagesStmts MessageDecls TransitionStmts '}' ';'"""
protocol = Protocol(locFromTok(p, 2))
protocol.name = p[3]
protocol.sendSemantics = p[1]
protocol.manager = p[5]
protocol.addManagesStmts(p[6])
protocol.addMessageDecls(p[7])
protocol.addTransitionStmts(p[8])
p[0] = protocol
def p_ManagesStmts(p):
"""ManagesStmts : ManagesStmts ManagesStmt
| """
if 1 == len(p):
p[0] = [ ]
else:
p[1].append(p[2])
p[0] = p[1]
def p_ManagerStmtOpt(p):
"""ManagerStmtOpt : MANAGER ID ';'
| """
if 1 == len(p):
p[0] = None
else:
p[0] = ManagerStmt(locFromTok(p, 1), p[2])
def p_ManagesStmt(p):
"""ManagesStmt : MANAGES ID ';'"""
p[0] = ManagesStmt(locFromTok(p, 1), p[2])
def p_MessageDecls(p):
"""MessageDecls : MessageDecls MessageDecl ';'
| MessageDecl ';'"""
if 3 == len(p):
p[0] = [ p[1] ]
else:
p[1].append(p[2])
p[0] = p[1]
def p_MessageDecl(p):
"""MessageDecl : SendSemanticsQual DirectionQual MessageBody"""
msg = p[3]
msg.sendSemantics = p[1]
msg.direction = p[2]
p[0] = msg
def p_MessageBody(p):
"""MessageBody : MessageId MessageInParams MessageOutParams"""
# FIXME/cjones: need better loc info: use one of the quals
msg = MessageDecl(locFromTok(p, 1))
msg.name = p[1]
msg.addInParams(p[2])
msg.addOutParams(p[3])
p[0] = msg
def p_MessageId(p):
"""MessageId : ID
| '~' ID"""
if 3 == len(p):
p[1] += p[2] # munge dtor name to "~Foo". handled later
p[0] = p[1]
def p_MessageInParams(p):
"""MessageInParams : '(' ParamList ')'"""
p[0] = p[2]
def p_MessageOutParams(p):
"""MessageOutParams : RETURNS '(' ParamList ')'
| """
if 1 == len(p):
p[0] = [ ]
else:
p[0] = p[3]
def p_TransitionStmts(p):
"""TransitionStmts : """
# FIXME/cjones: impl
p[0] = [ ]
##--------------------
## Minor stuff
def p_SendSemanticsQual(p):
"""SendSemanticsQual : ASYNC
| RPC
| SYNC
| """
if 1 == len(p):
p[0] = ASYNC
return
s = p[1]
if 'async' == s: p[0] = ASYNC
elif 'rpc' == s: p[0] = RPC
elif 'sync'== s: p[0] = SYNC
else:
assert 0
def p_DirectionQual(p):
"""DirectionQual : IN
| INOUT
| OUT"""
s = p[1]
if 'in' == s: p[0] = IN
elif 'inout' == s: p[0] = INOUT
elif 'out' == s: p[0] = OUT
else:
assert 0
def p_ParamList(p):
"""ParamList : ParamList ',' Param
| Param
| """
if 1 == len(p):
p[0] = [ ]
elif 2 == len(p):
p[0] = [ p[1] ]
else:
p[1].append(p[3])
p[0] = p[1]
def p_Param(p):
"""Param : ID ID"""
loc = locFromTok(p, 1)
p[0] = Param(loc,
TypeSpec(loc, QualifiedId(loc, p[1])),
p[2])
##--------------------
## C++ stuff
def p_CxxType(p):
"""CxxType : QualifiedID
| ID"""
if isinstance(p[0], QualifiedId):
p[0] = TypeSpec(p[1].loc, p[1])
else:
loc = locFromTok(p, 1)
p[0] = TypeSpec(loc, QualifiedId(loc, p[1]))
def p_QualifiedID(p):
"""QualifiedID : QualifiedID COLONCOLON ID
| ID COLONCOLON ID"""
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"'% (
includeStackStr, Loc(Parser.current.filename, t.lineno), t.value)

679
ipc/ipdl/ipdl/type.py Normal file
View File

@ -0,0 +1,679 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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
import ipdl.builtin as builtin
class Type:
# Is this a C++ type?
def isCxx():
return False
# Is this an IPDL type?
def isIPDL():
return False
# Can this type appear in IPDL programs?
def isVisible():
return False
def isVoid(self):
return False
def typename(self):
return self.__class__.__name__
def name(self): raise Exception, 'NYI'
def fullname(self): raise Exception, 'NYI'
class VoidType(Type):
# the following are not a type-o's (hah): void is both a Cxx and IPDL type
def isCxx():
return True
def isIPDL():
return True
def isVisible(self):
return True
def isVoid(self):
return True
def name(self): return 'void'
def fullname(self): return 'void'
VOID = VoidType()
##--------------------
class CxxType(Type):
def isCxx(self):
return True
def isBuiltin(self):
return False
def isImported(self):
return False
def isGenerated(self):
return False
def isVisible(self):
return True
class BuiltinCxxType(CxxType):
def __init__(self, qname):
assert isinstance(qname, QualifiedId)
self.loc = qname.loc
self.qname = qname
def isBuiltin(self): return True
def name(self):
return self.qname.baseid
def fullname(self):
return str(self.qname)
class ImportedCxxType(CxxType):
def __init__(self, qname):
assert isinstance(qname, QualifiedId)
self.loc = qname.loc
self.qname = qname
def isImported(self): return True
def name(self):
return self.qname.baseid
def fullname(self):
return str(self.qname)
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 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 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
self.params = [ ]
self.returns = [ ]
self.ctor = ctor
self.dtor = dtor
self.cdtype = cdtype
def isMessage(self): return True
def isCtor(self): return self.ctor
def isDtor(self): return self.dtor
def constructedType(self): return self.cdtype
def isIn(self): return self.direction is IN
def isOut(self): return self.direction is OUT
def isInout(self): return self.direction is INOUT
class ProtocolType(IPDLType):
def __init__(self, qname, sendSemantics):
self.qname = qname
self.sendSemantics = sendSemantics
self.manager = None
self.manages = [ ]
def isProtocol(self): return True
def name(self):
return self.qname.baseid
def fullname(self):
return str(self.qname)
def managedBy(self, mgr):
self.manager = mgr
def isManager(self, pt):
for managed in self.manages:
if pt is managed:
return True
return False
def isManaged(self):
return self.manager is not None
def isToplevel(self):
return not self.isManaged()
##--------------------
_builtinloc = Loc('<builtin>', 0)
def makeBuiltinUsing(tname):
quals = tname.split('::')
base = quals.pop()
quals = quals[0:]
return UsingStmt(_builtinloc,
TypeSpec(_builtinloc,
QualifiedId(_builtinloc, base, quals)))
builtinUsing = [ makeBuiltinUsing(t) for t in builtin.Types ]
builtinIncludes = [ CxxInclude(_builtinloc, f) for f in builtin.Includes ]
##--------------------
def errormsg(loc, fmt, *args):
while not isinstance(loc, Loc):
if loc is None: loc = Loc.NONE
else: loc = loc.loc
return '%s: error: %s'% (str(loc), fmt % args)
class SymbolTable:
def __init__(self, errors):
self.errors = errors
self.scopes = [ { } ] # stack({})
self.globalScope = self.scopes[0]
self.currentScope = self.globalScope
def enterScope(self, node):
assert (isinstance(self.scopes[0], dict)
and self.globalScope is self.scopes[0])
assert (isinstance(self.currentScope, dict))
if not hasattr(node, 'symtab'):
node.symtab = { }
self.scopes.append(node.symtab)
self.currentScope = self.scopes[-1]
def exitScope(self, node):
symtab = self.scopes.pop()
assert self.currentScope is symtab
self.currentScope = self.scopes[-1]
assert (isinstance(self.scopes[0], dict)
and self.globalScope is self.scopes[0])
assert isinstance(self.currentScope, dict)
def lookup(self, sym):
# NB: since IPDL doesn't allow any aliased names of different types,
# it doesn't matter in which order we walk the scope chain to resolve
# |sym|
for scope in self.scopes:
decl = scope.get(sym, None)
if decl is not None: return decl
return None
def declare(self, decl):
assert decl.progname or decl.shortname or decl.fullname
assert decl.loc
assert decl.type
def tryadd(name):
olddecl = self.lookup(name)
if olddecl is not None:
self.errors.append(errormsg(
decl.loc,
"redeclaration of symbol `%s', first declared at %s",
name, olddecl.loc))
return
self.currentScope[name] = decl
decl.scope = self.currentScope
if decl.progname: tryadd(decl.progname)
if decl.shortname: tryadd(decl.shortname)
if decl.fullname: tryadd(decl.fullname)
class TypeCheck(Visitor):
'''This pass sets the .type attribute of every AST node. For some
nodes, the type is meaningless and it is set to "VOID." This pass
also sets the .decl attribute of AST nodes for which that is relevant;
a decl says where, with what type, and under what name(s) a node was
declared.
With this information, it finally type checks the AST.'''
def __init__(self):
# NB: no IPDL compile will EVER print a warning. A program has
# one of two attributes: it is either well typed, or not well typed.
self.errors = [ ] # [ string ]
self.symtab = SymbolTable(self.errors)
def check(self, tu, errout=sys.stderr):
tu.cxxIncludes = builtinIncludes + tu.cxxIncludes
# tag each relevant node with "decl" information, giving type, name,
# and location of declaration
tu.accept(GatherDecls(builtinUsing, self.symtab, self.errors))
if len(self.errors):
# no point in checking types if we couldn't even resolve symbols
self.reportErrors(errout)
return False
# now that the nodes have decls, type checking is much easier.
tu.accept(CheckTypes(self.symtab, self.errors))
if (len(self.errors)):
# no point in later passes if type checking fails
self.reportErrors(errout)
return False
return True
def reportErrors(self, errout):
for error in self.errors:
print >>errout, error
class GatherDecls(Visitor):
def __init__(self, builtinUsing, symtab, errors):
self.builtinUsing = builtinUsing
self.symtab = symtab
self.errors = errors
self.visited = set() # set(filename)
self.depth = 0
def visitTranslationUnit(self, tu):
# all TranslationUnits declare symbols in global scope
if tu.filename in self.visited:
return
self.visited.add(tu.filename)
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
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)
self.symtab.declare(udecl)
using.decl = udecl
p = tu.protocol
# FIXME/cjones: it's a little weird and counterintuitive to put
# both the namespace and non-namespaced name in the global scope.
# try to figure out something better; maybe a type-neutral |using|
# that works for C++ and protocol types?
pdecl = Decl(p.loc)
pdecl.shortname = p.name
fullname = QualifiedId(p.loc, p.name,
[ ns.namespace for ns in p.namespaces ])
if len(fullname.quals):
pdecl.fullname = str(fullname)
pdecl.type = ProtocolType(fullname, p.sendSemantics)
pdecl.body = p
self.symtab.declare(pdecl)
p.decl = pdecl
p.type = pdecl.type
# make sure we have decls for all dependent protocols
for pinc in tu.protocolIncludes:
pinc.accept(self)
# 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
# grab symbols in the protocol itself
p.accept(self)
self.symtab.exitScope(tu)
tu.type = VOID
self.depth -= 1
def visitProtocolInclude(self, pi):
pi.tu.accept(self)
def visitUsingStmt(self, using):
decl = Decl(using.loc)
decl.shortname = using.type.basename()
fullname = str(using.type)
if decl.shortname != fullname:
decl.fullname = fullname
decl.type = ImportedCxxType(using.type.spec)
self.symtab.declare(decl)
using.decl = decl
def visitProtocol(self, p):
# protocol scope
self.symtab.enterScope(p)
if p.manager is not None:
p.manager.of = p
p.manager.accept(self)
for managed in p.managesStmts:
managed.manager = p
managed.accept(self)
setattr(self, 'currentProtocolDecl', p.decl)
for msg in p.messageDecls:
msg.accept(self)
del self.currentProtocolDecl
for trans in p.transitionStmts:
trans.accept(self)
# 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
self.symtab.exitScope(p)
def visitManagerStmt(self, mgr):
mgrdecl = self.symtab.lookup(mgr.name)
pdecl = mgr.of.decl
assert pdecl
pname, mgrname = pdecl.shortname, mgr.name
loc = mgr.loc
if mgrdecl is None:
self.errors.append(
errmsg(
loc,
"protocol `%s' referenced as |manager| of `%s' has not been declared",
mgrname, pname))
elif not isinstance(mgrdecl.type, ProtocolType):
self.errors.append(
errmsg(
loc,
"entity `%s' referenced as |manager| of `%s' is not of `protocol' type; instead it is of type `%s'",
mgrname, pname, mgrdecl.type.typename()))
else:
assert pdecl.type.manager is None
mgr.decl = mgrdecl
pdecl.type.manager = mgrdecl.type
def visitManagesStmt(self, mgs):
mgsdecl = self.symtab.lookup(mgs.name)
pdecl = mgs.manager.decl
assert pdecl
pname, mgsname = pdecl.shortname, mgs.name
loc = mgs.loc
if mgsdecl is None:
self.errors.append(
errormsg(
loc,
"protocol `%s', managed by `%s', has not been declared",
mgsname, pdeclname))
elif not isinstance(mgsdecl.type, ProtocolType):
self.errors.append(
errormsg(
loc,
"%s declares itself managing a non-`protocol' entity `%s' of type `%s'",
pdeclname, mgsname, mgsdecl.type.typename()))
else:
mgs.decl = mgsdecl
pdecl.type.manages.append(mgsdecl.type)
def visitMessageDecl(self, md):
msgname = md.name
loc = md.loc
isctor = False
isdtor = False
cdtype = None
if '~' == msgname[0]:
# it's a destructor. look up the constructed type
msgname = msgname[1:]
decl = self.symtab.lookup(msgname)
if decl is None:
self.errors.append(
errormsg(
loc,
"type `%s' has not been declared",
msgname))
elif not decl.type.isProtocol():
self.errors.append(
errormsg(
loc,
"destructor for non-protocol type `%s'",
msgname))
else:
msgname += 'Destructor'
isdtor = True
cdtype = decl.type
decl = self.symtab.lookup(msgname)
if decl is not None and decl.type.isProtocol():
# probably a ctor. we'll check validity later.
msgname += 'Constructor'
isctor = True
cdtype = decl.type
elif decl is not None:
self.errors.append(
errormsg(
loc,
"message name `%s' already declared as `%s'",
msgname, decl.type.typename()))
# if we error here, no big deal; move on to find more
decl = None
# enter message scope
self.symtab.enterScope(md)
msgtype = MessageType(md.sendSemantics, md.direction,
ctor=isctor, dtor=isdtor, cdtype=cdtype)
# replace inparam Param nodes with proper Decls
for i, inparam in enumerate(md.inParams):
inptname = inparam.typespec.basename()
inploc = inparam.typespec.loc
inptdecl = self.symtab.lookup(inptname)
if inptdecl is None:
self.errors.append(
errormsg(
inploc,
"inparam typename `%s' of message `%s' has not been declared",
inptname, msgname))
else:
inpdecl = Decl(inploc)
inpdecl.progname = inparam.name
inpdecl.type = inptdecl.type
self.symtab.declare(inpdecl)
msgtype.params.append(inpdecl.type)
md.inParams[i] = inpdecl
# replace outparam Param with proper Decls
for i, outparam in enumerate(md.outParams):
outptname = outparam.typespec.basename()
outploc = outparam.typespec.loc
outptdecl = self.symtab.lookup(outptname)
if outptdecl is None:
self.errors.append(
errormsg(
outploc,
"outparam typename `%s' of message `%s' has not been declared",
outptname, msgname))
else:
outpdecl = Decl(outploc)
outpdecl.progname = outparam.name
outpdecl.type = outptdecl.type
self.symtab.declare(outpdecl)
msgtype.returns.append(outpdecl.type)
md.outParams[i] = outpdecl
self.symtab.exitScope(md)
decl = Decl(loc)
decl.progname = msgname
decl.type = msgtype
self.symtab.declare(decl)
md.decl = decl
md.protocolDecl = self.currentProtocolDecl
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:
return
self.visited.add(inc.tu.filename)
inc.tu.protocol.accept(self)
def visitProtocol(self, p):
# check that we require no more "power" than our manager protocol
ptype, pname = p.decl.type, p.decl.shortname
mgrtype = ptype.manager
if mgrtype is not None and ptype.needsMoreJuiceThan(mgrtype):
mgrname = p.manager.decl.shortname
self.errors.append(errormsg(
p.decl.loc,
"protocol `%s' requires more powerful send semantics than its manager `%s' provides",
pname,
mgrname))
return Visitor.visitProtocol(self, p)
def visitManagesStmt(self, mgs):
pdecl = mgs.manager.decl
ptype, pname = pdecl.type, pdecl.shortname
mgsdecl = mgs.decl
mgstype, mgsname = mgsdecl.type, mgsdecl.shortname
loc = mgs.loc
# we added this information; sanity check it
for managed in ptype.manages:
if managed is mgstype:
break
else:
assert False
# check that the "managed" protocol agrees
if mgstype.manager is not ptype:
self.errors.append(errormsg(
loc,
"|manages| declaration in protocol `%s' does not match any |manager| declaration in protocol `%s'",
pname, mgsname))
def visitManagerStmt(self, mgr):
pdecl = mgr.of.decl
ptype, pname = pdecl.type, pdecl.shortname
mgrdecl = mgr.decl
mgrtype, mgrname = mgrdecl.type, mgrdecl.shortname
# we added this information; sanity check it
assert ptype.manager is mgrtype
loc = mgr.loc
# check that the "manager" protocol agrees
if not mgrtype.isManager(ptype):
self.errors.append(errormsg(
loc,
"|manager| declaration in protocol `%s' does not match any |manages| declaration in protocol `%s'",
pname, mgrname))
def visitMessageDecl(self, md):
mtype, mname = md.decl.type, md.decl.progname
ptype, pname = md.protocolDecl.type, md.protocolDecl.shortname
loc = md.decl.loc
if mtype.needsMoreJuiceThan(ptype):
self.errors.append(errormsg(
loc,
"message `%s' requires more powerful send semantics than its protocol `%s' provides",
mname,
pname))
if mtype.isAsync() and len(mtype.returns):
# XXX/cjones could modify grammar to disallow this ...
self.errors.append(errormsg(
loc,
"asynchronous message `%s' requests returned values",
mname))
if (mtype.isCtor() or mtype.isDtor()) and not ptype.isManager(mtype.constructedType()):
self.errors.append(errormsg(
loc,
"ctor/dtor for protocol `%s', which is not managed by protocol `%s'",
mname[:-len('constructor')],
pname))

141
ipc/ipdl/ipdlc Executable file
View File

@ -0,0 +1,141 @@
#!/usr/bin/env python
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is mozilla.org code.
#
# Contributor(s):
# Chris Jones <jones.chris.g@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either of the GNU General Public License Version 2 or later (the "GPL"),
# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# 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 getopt, os, re, sys
_verbosity = 1
def log(minv, fmt, *args):
if _verbosity >= minv:
print >>sys.stderr, fmt % args
def getcallerpath():
'''Return the absolute path of the file containing the code that
**CALLED** this function.'''
return os.path.abspath(sys._getframe(1).f_code.co_filename)
# auto-add the compiler modules to the pythonpath
installdir, _ = os.path.split(getcallerpath())
sys.path.append(installdir)
import ipdl
# process command line
def usage(err):
if err:
out, rv = (sys.stderr, 1)
else:
out, rv = (sys.stdout, 0)
print >>out, '''
Usage: ipdlc [OPTIONS...] SPECIFICATION
where OPTIONS are zero or more of
-h, --help
: print this message and exit
-d DIR, --output-dir=DIR
: directory in which to put generated code.
Created if it doesn't exist.
default: use current working directory.
-v N, --verbosity=N
: be as verbose as N. set to 0 for no output, and higher than 1
for more output.
default: -v 1
and SPECIFICATION is a single IPDL specification file. '-' = read from stdin.
default : read specification from stdin
'''
sys.exit(rv)
try:
opts, args = getopt.gnu_getopt(
sys.argv[1:],
'd:hv:',
['help',
'output-dir=',
'verbosity='])
except getopt.GetoptError, err:
print >>sys.stderr, str(err), '\n'
usage(err=True)
codedir = os.getcwd()
spec = '-'
for o, a in opts:
if o in ('-h', '--help'):
usage(err=False)
elif o in ('-d', '--output-dir'):
codedir = a
elif o in ('-v', '--verbosity'):
_verbosity = int(a)
if 1 < len(args):
usage(err=True)
if 1 == len(args):
spec = args[0]
specstring = None
specfilename = None
if spec != '-':
log (1, 'using specification %s', spec)
specstring = open(spec, 'r').read()
specfilename = spec
else:
log(1, 'reading specification from stdin')
specstring = sys.stdin.read()
specfilename = '<stdin>'
log(3, ' specification:\n%s', specstring)
log(1, 'parsing specification')
ast = ipdl.parse(specstring, specfilename)
log(1, 'checking types')
if not ipdl.typecheck(ast):
print >>sys.stderr, 'Specification is not well typed.'
sys.exit(1)
if _verbosity >= 3:
log(3, ' pretty printed code:')
ipdl.genipdl(ast, codedir)
log(1, 'generating code to "%s"', codedir)
ipdl.gencxx(ast, codedir)
log(1, '''\nIMPORTANT: remember to add the new enum value
%sMsgStart,
to the |IPCMessageStart| enum in "ipc/glue/IPCMessageUtils.h".
Your code will not compile until this value is added.
'''% (ast.protocol.name))