diff options
Diffstat (limited to 'ipc/ipdl/ipdl/cxx')
-rw-r--r-- | ipc/ipdl/ipdl/cxx/__init__.py | 3 | ||||
-rw-r--r-- | ipc/ipdl/ipdl/cxx/ast.py | 1033 | ||||
-rw-r--r-- | ipc/ipdl/ipdl/cxx/cgen.py | 557 | ||||
-rw-r--r-- | ipc/ipdl/ipdl/cxx/code.py | 187 |
4 files changed, 1780 insertions, 0 deletions
diff --git a/ipc/ipdl/ipdl/cxx/__init__.py b/ipc/ipdl/ipdl/cxx/__init__.py new file mode 100644 index 0000000000..6fbe8159b2 --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/__init__.py @@ -0,0 +1,3 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/ipc/ipdl/ipdl/cxx/ast.py b/ipc/ipdl/ipdl/cxx/ast.py new file mode 100644 index 0000000000..3b9448859b --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/ast.py @@ -0,0 +1,1033 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import copy +import functools + +from ipdl.util import hash_str + + +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 visitVerbatimNode(self, verb): + pass + + def visitGroupNode(self, group): + for node in group.nodes: + node.accept(self) + + 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 visitTypeArray(self, ta): + ta.basetype.accept(self) + ta.nmemb.accept(self) + + def visitTypeEnum(self, enum): + pass + + def visitTypeFunction(self, fn): + pass + + def visitTypeUnion(self, union): + for t, name in union.components: + t.accept(self) + + def visitTypedef(self, tdef): + tdef.fromtype.accept(self) + + def visitUsing(self, us): + us.type.accept(self) + + def visitForwardDecl(self, fd): + pass + + def visitDecl(self, decl): + decl.type.accept(self) + + def visitParam(self, param): + self.visitDecl(param) + if param.default is not None: + param.default.accept(self) + + def visitClass(self, cls): + for inherit in cls.inherits: + inherit.accept(self) + self.visitBlock(cls) + + def visitInherit(self, inh): + pass + + def visitFriendClassDecl(self, fcd): + pass + + def visitMethodDecl(self, meth): + for param in meth.params: + param.accept(self) + if meth.ret is not None: + meth.ret.accept(self) + if meth.typeop is not None: + meth.typeop.accept(self) + if meth.T is not None: + meth.T.accept(self) + + def visitMethodDefn(self, meth): + meth.decl.accept(self) + self.visitBlock(meth) + + def visitFunctionDecl(self, fun): + self.visitMethodDecl(fun) + + def visitFunctionDefn(self, fd): + self.visitMethodDefn(fd) + + 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 visitExprLiteral(self, l): + pass + + def visitExprVar(self, v): + pass + + def visitExprPrefixUnop(self, e): + e.expr.accept(self) + + def visitExprBinary(self, e): + e.left.accept(self) + e.right.accept(self) + + def visitExprConditional(self, c): + c.cond.accept(self) + c.ife.accept(self) + c.elsee.accept(self) + + def visitExprAddrOf(self, eao): + self.visitExprPrefixUnop(eao) + + def visitExprDeref(self, ed): + self.visitExprPrefixUnop(ed) + + def visitExprNot(self, en): + self.visitExprPrefixUnop(en) + + def visitExprCast(self, ec): + ec.expr.accept(self) + + 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 visitExprMove(self, ec): + self.visitExprCall(ec) + + def visitExprNothing(self, ec): + self.visitExprCall(ec) + + def visitExprSome(self, ec): + self.visitExprCall(ec) + + def visitExprNew(self, en): + en.ctype.accept(self) + if en.newargs is not None: + for arg in en.newargs: + arg.accept(self) + if en.args is not None: + for arg in en.args: + arg.accept(self) + + def visitExprDelete(self, ed): + ed.obj.accept(self) + + def visitExprMemberInit(self, minit): + self.visitExprCall(minit) + + def visitExprLambda(self, l): + self.visitBlock(l) + + def visitStmtBlock(self, sb): + self.visitBlock(sb) + + def visitStmtDecl(self, sd): + sd.decl.accept(self) + if sd.init is not None: + sd.init.accept(self) + + def visitLabel(self, label): + pass + + def visitCaseLabel(self, case): + pass + + def visitDefaultLabel(self, dl): + pass + + def visitStmtIf(self, si): + si.cond.accept(self) + si.ifb.accept(self) + if si.elseb is not None: + si.elseb.accept(self) + + def visitStmtFor(self, sf): + if sf.init is not None: + sf.init.accept(self) + if sf.cond is not None: + sf.cond.accept(self) + if sf.update is not None: + sf.update.accept(self) + + def visitStmtSwitch(self, ss): + ss.expr.accept(self) + self.visitBlock(ss) + + def visitStmtBreak(self, sb): + pass + + 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, indent=False): + Node.__init__(self) + self.ws = ws + self.indent = indent + + +Whitespace.NL = Whitespace("\n") + + +class VerbatimNode(Node): + # A block of text to be written verbatim to the output file. + # + # NOTE: This node is usually created by `code`. See `code.py` for details. + # FIXME: Merge Whitespace and VerbatimNode? They're identical. + def __init__(self, text, indent=0): + Node.__init__(self) + self.text = text + self.indent = indent + + +class GroupNode(Node): + # A group of nodes to be treated as a single node. These nodes have an + # optional indentation level which should be applied when generating them. + # + # NOTE: This node is usually created by `code`. See `code.py` for details. + def __init__(self, nodes, offset=0): + Node.__init__(self) + self.nodes = nodes + self.offset = offset + + +class File(Node): + def __init__(self, filename): + Node.__init__(self) + self.name = filename + # array of stuff in the file --- stmts and preprocessor thingies + self.stuff = [] + + def addthing(self, thing): + assert thing is not None + assert not isinstance(thing, list) + self.stuff.append(thing) + + def addthings(self, things): + for t in things: + self.addthing(t) + + # "look like" a Block so code doesn't have to care whether they're + # in global scope or not + def addstmt(self, stmt): + assert stmt is not None + assert not isinstance(stmt, list) + self.stuff.append(stmt) + + def addstmts(self, stmts): + for s in stmts: + self.addstmt(s) + + def addcode(self, tmpl, **context): + from ipdl.cxx.code import StmtCode + + self.addstmt(StmtCode(tmpl, **context)) + + +class CppDirective(Node): + """represents |#[directive] [rest]|, where |rest| is any string""" + + def __init__(self, directive, rest=None): + Node.__init__(self) + self.directive = directive + self.rest = rest + + +class Block(Node): + def __init__(self): + Node.__init__(self) + self.stmts = [] + + def addstmt(self, stmt): + assert stmt is not None + assert not isinstance(stmt, tuple) + self.stmts.append(stmt) + + def addstmts(self, stmts): + for s in stmts: + self.addstmt(s) + + def addcode(self, tmpl, **context): + from ipdl.cxx.code import StmtCode + + self.addstmt(StmtCode(tmpl, **context)) + + +# ------------------------------ +# type and decl thingies + + +class Namespace(Block): + def __init__(self, name): + assert isinstance(name, str) + + Block.__init__(self) + self.name = name + + +class Type(Node): + def __init__( + self, + name, + const=False, + ptr=False, + ptrptr=False, + ptrconstptr=False, + ref=False, + rvalref=False, + rightconst=False, + hasimplicitcopyctor=True, + T=None, + inner=None, + ): + """ + Represents the type |name<T>::inner| with the ptr and const + modifiers as specified. + + To avoid getting fancy with recursive types, we limit the kinds + of pointer types that can be be constructed. + + ptr => T* + ptrptr => T** + ptrconstptr => T* const* + ref => T& + rvalref => T&& + + Any type, naked or pointer, can be const (const T) or ref (T&).""" + # XXX(nika): This type is complex enough at this point, perhaps we + # should get "fancy with recursive types" to simplify it. + assert isinstance(name, str) + assert isinstance(const, bool) + assert isinstance(ptr, bool) + assert isinstance(ptrptr, bool) + assert isinstance(ptrconstptr, bool) + assert isinstance(ref, bool) + assert isinstance(rvalref, bool) + assert isinstance(rightconst, bool) + assert not isinstance(T, str) + + Node.__init__(self) + self.name = name + self.const = const + self.ptr = ptr + self.ptrptr = ptrptr + self.ptrconstptr = ptrconstptr + self.ref = ref + self.rvalref = rvalref + self.rightconst = rightconst + self.hasimplicitcopyctor = hasimplicitcopyctor + self.T = T + self.inner = inner + # XXX could get serious here with recursive types, but shouldn't + # need that for this codegen + + def __deepcopy__(self, memo): + return Type( + self.name, + const=self.const, + ptr=self.ptr, + ptrptr=self.ptrptr, + ptrconstptr=self.ptrconstptr, + ref=self.ref, + rvalref=self.rvalref, + rightconst=self.rightconst, + T=copy.deepcopy(self.T, memo), + inner=copy.deepcopy(self.inner, memo), + ) + + +Type.BOOL = Type("bool") +Type.INT = Type("int") +Type.INT32 = Type("int32_t") +Type.INTPTR = Type("intptr_t") +Type.NSRESULT = Type("nsresult") +Type.UINT32 = Type("uint32_t") +Type.UINT32PTR = Type("uint32_t", ptr=True) +Type.SIZE = Type("size_t") +Type.VOID = Type("void") +Type.VOIDPTR = Type("void", ptr=True) +Type.AUTO = Type("auto") +Type.AUTORVAL = Type("auto", rvalref=True) + + +class TypeArray(Node): + def __init__(self, basetype, nmemb): + """the type |basetype DECLNAME[nmemb]|. |nmemb| is an Expr""" + self.basetype = basetype + self.nmemb = nmemb + + +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 TypeUnion(Node): + def __init__(self, name=None): + Node.__init__(self) + self.name = name + self.components = [] # [ Decl ] + + def addComponent(self, type, name): + self.components.append(Decl(type, name)) + + +class TypeFunction(Node): + def __init__(self, params=[], ret=Type("void")): + """Anonymous function type std::function<>""" + self.params = params + self.ret = ret + + +@functools.total_ordering +class Typedef(Node): + def __init__(self, fromtype, totypename, templateargs=[]): + assert isinstance(totypename, str) + + Node.__init__(self) + self.fromtype = fromtype + self.totypename = totypename + self.templateargs = templateargs + + def __lt__(self, other): + return self.totypename < other.totypename + + def __eq__(self, other): + return self.__class__ == other.__class__ and self.totypename == other.totypename + + def __hash__(self): + return hash_str(self.totypename) + + +class Using(Node): + def __init__(self, type): + Node.__init__(self) + self.type = type + + +class ForwardDecl(Node): + def __init__(self, pqname, cls=False, struct=False): + # Exactly one of cls and struct must be set + assert cls ^ struct + + self.pqname = pqname + self.cls = cls + self.struct = struct + + +class Decl(Node): + """represents |Foo bar|, e.g. in a function signature""" + + def __init__(self, type, name): + assert type is not None + assert not isinstance(type, str) + assert isinstance(name, str) + + Node.__init__(self) + self.type = type + self.name = name + + def __deepcopy__(self, memo): + return Decl(copy.deepcopy(self.type, memo), self.name) + + +class Param(Decl): + def __init__(self, type, name, default=None): + Decl.__init__(self, type, name) + self.default = default + + def __deepcopy__(self, memo): + return Param( + copy.deepcopy(self.type, memo), self.name, copy.deepcopy(self.default, memo) + ) + + +# ------------------------------ +# class stuff + + +class Class(Block): + def __init__( + self, + name, + inherits=[], + interface=False, + abstract=False, + final=False, + specializes=None, + struct=False, + ): + assert not (interface and abstract) + assert not (abstract and final) + assert not (interface and final) + assert not (inherits and specializes) + + Block.__init__(self) + self.name = name + self.inherits = inherits # [ Type ] + self.interface = interface # bool + self.abstract = abstract # bool + self.final = final # bool + self.specializes = specializes # Type or None + self.struct = struct # bool + + +class Inherit(Node): + def __init__(self, type, viz="public"): + assert isinstance(viz, str) + Node.__init__(self) + self.type = type + self.viz = viz + + +class FriendClassDecl(Node): + def __init__(self, friend): + Node.__init__(self) + self.friend = friend + + +# Python2 polyfill for Python3's Enum() functional API. + + +def make_enum(name, members_str): + members_list = members_str.split() + members_dict = {} + for member_value, member in enumerate(members_list, start=1): + members_dict[member] = member_value + return type(name, (), members_dict) + + +MethodSpec = make_enum("MethodSpec", "NONE VIRTUAL PURE OVERRIDE STATIC") + + +class MethodDecl(Node): + def __init__( + self, + name, + params=[], + ret=Type("void"), + methodspec=MethodSpec.NONE, + const=False, + warn_unused=False, + force_inline=False, + typeop=None, + T=None, + cls=None, + ): + assert not (name and typeop) + assert name is None or isinstance(name, str) + assert not isinstance(ret, list) + for decl in params: + assert not isinstance(decl, str) + assert not isinstance(T, int) + assert isinstance(const, bool) + assert isinstance(warn_unused, bool) + assert isinstance(force_inline, bool) + + if typeop is not None: + assert methodspec == MethodSpec.NONE + ret = None + + Node.__init__(self) + self.name = name + self.params = params # [ Param ] + self.ret = ret # Type or None + self.methodspec = methodspec # enum + self.const = const # bool + self.warn_unused = warn_unused # bool + self.force_inline = force_inline or bool(T) # bool + self.typeop = typeop # Type or None + self.T = T # Type or None + self.cls = cls # Class or None + self.only_for_definition = False + + def __deepcopy__(self, memo): + return MethodDecl( + self.name, + params=copy.deepcopy(self.params, memo), + ret=copy.deepcopy(self.ret, memo), + methodspec=self.methodspec, + const=self.const, + warn_unused=self.warn_unused, + force_inline=self.force_inline, + typeop=copy.deepcopy(self.typeop, memo), + T=copy.deepcopy(self.T, memo), + ) + + +class MethodDefn(Block): + def __init__(self, decl): + Block.__init__(self) + self.decl = decl + + +class FunctionDecl(MethodDecl): + def __init__( + self, + name, + params=[], + ret=Type("void"), + methodspec=MethodSpec.NONE, + warn_unused=False, + force_inline=False, + T=None, + ): + assert methodspec == MethodSpec.NONE or methodspec == MethodSpec.STATIC + MethodDecl.__init__( + self, + name, + params=params, + ret=ret, + methodspec=methodspec, + warn_unused=warn_unused, + force_inline=force_inline, + T=T, + ) + + +class FunctionDefn(MethodDefn): + def __init__(self, decl): + MethodDefn.__init__(self, decl) + + +class ConstructorDecl(MethodDecl): + def __init__(self, name, params=[], explicit=False, force_inline=False): + MethodDecl.__init__( + self, name, params=params, ret=None, force_inline=force_inline + ) + self.explicit = explicit + + def __deepcopy__(self, memo): + return ConstructorDecl( + self.name, copy.deepcopy(self.params, memo), self.explicit + ) + + +class ConstructorDefn(MethodDefn): + def __init__(self, decl, memberinits=[]): + MethodDefn.__init__(self, decl) + self.memberinits = memberinits + + +class DestructorDecl(MethodDecl): + def __init__(self, name, methodspec=MethodSpec.NONE, force_inline=False): + # C++ allows pure or override destructors, but ipdl cgen does not. + assert methodspec == MethodSpec.NONE or methodspec == MethodSpec.VIRTUAL + MethodDecl.__init__( + self, + name, + params=[], + ret=None, + methodspec=methodspec, + force_inline=force_inline, + ) + + def __deepcopy__(self, memo): + return DestructorDecl( + self.name, methodspec=self.methodspec, force_inline=self.force_inline + ) + + +class DestructorDefn(MethodDefn): + def __init__(self, decl): + MethodDefn.__init__(self, decl) + + +# ------------------------------ +# expressions + + +class ExprVar(Node): + def __init__(self, name): + assert isinstance(name, str) + + Node.__init__(self) + self.name = name + + +ExprVar.THIS = ExprVar("this") + + +class ExprLiteral(Node): + def __init__(self, value, type): + """|type| is a Python format specifier; 'd' for example""" + Node.__init__(self) + self.value = value + self.type = type + + @staticmethod + def Int(i): + return ExprLiteral(i, "d") + + @staticmethod + def String(s): + return ExprLiteral('"' + s + '"', "s") + + def __str__(self): + return ("%" + self.type) % (self.value) + + +ExprLiteral.ZERO = ExprLiteral.Int(0) +ExprLiteral.ONE = ExprLiteral.Int(1) +ExprLiteral.NULL = ExprVar("nullptr") +ExprLiteral.TRUE = ExprVar("true") +ExprLiteral.FALSE = ExprVar("false") + + +class ExprPrefixUnop(Node): + def __init__(self, expr, op): + assert not isinstance(expr, tuple) + self.expr = expr + self.op = op + + +class ExprNot(ExprPrefixUnop): + def __init__(self, expr): + ExprPrefixUnop.__init__(self, expr, "!") + + +class ExprAddrOf(ExprPrefixUnop): + def __init__(self, expr): + ExprPrefixUnop.__init__(self, expr, "&") + + +class ExprDeref(ExprPrefixUnop): + def __init__(self, expr): + ExprPrefixUnop.__init__(self, expr, "*") + + +class ExprCast(Node): + def __init__(self, expr, type, static=False, const=False): + # Exactly one of these should be set + assert static ^ const + + Node.__init__(self) + self.expr = expr + self.type = type + self.static = static + self.const = const + + +class ExprBinary(Node): + def __init__(self, left, op, right): + Node.__init__(self) + self.left = left + self.op = op + self.right = right + + +class ExprConditional(Node): + def __init__(self, cond, ife, elsee): + Node.__init__(self) + self.cond = cond + self.ife = ife + self.elsee = elsee + + +class ExprSelect(Node): + def __init__(self, obj, op, field): + assert obj and op and field + assert not isinstance(obj, str) + assert isinstance(op, str) + + Node.__init__(self) + self.obj = obj + self.op = op + if isinstance(field, str): + self.field = ExprVar(field) + else: + self.field = field + + +class ExprAssn(Node): + def __init__(self, lhs, rhs, op="="): + Node.__init__(self) + self.lhs = lhs + self.op = op + self.rhs = rhs + + +class ExprCall(Node): + def __init__(self, func, args=[]): + assert hasattr(func, "accept") + assert isinstance(args, list) + for arg in args: + assert arg and not isinstance(arg, str) + + Node.__init__(self) + self.func = func + self.args = args + + +class ExprMove(ExprCall): + def __init__(self, arg): + ExprCall.__init__(self, ExprVar("std::move"), args=[arg]) + + +class ExprNew(Node): + # XXX taking some poetic license ... + def __init__(self, ctype, args=[], newargs=None): + assert not (ctype.const or ctype.ref or ctype.rvalref) + + Node.__init__(self) + self.ctype = ctype + self.args = args + self.newargs = newargs + + +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) + + +class ExprLambda(Block): + def __init__(self, captures=[], params=[], ret=None): + Block.__init__(self) + assert isinstance(captures, list) + assert isinstance(params, list) + self.captures = captures + self.params = params + self.ret = ret + + +# ------------------------------ +# statements etc. + + +class StmtBlock(Block): + def __init__(self, stmts=[]): + Block.__init__(self) + self.addstmts(stmts) + + +class StmtDecl(Node): + def __init__(self, decl, init=None, initargs=None): + assert not (init and initargs) + assert not isinstance(init, str) # easy to confuse with Decl + assert not isinstance(init, list) + assert not isinstance(decl, tuple) + + Node.__init__(self) + self.decl = decl + self.init = init + self.initargs = initargs + + +class Label(Node): + def __init__(self, name): + Node.__init__(self) + self.name = name + + +Label.PUBLIC = Label("public") +Label.PROTECTED = Label("protected") +Label.PRIVATE = Label("private") + + +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 addifstmts(self, stmts): + self.ifb.addstmts(stmts) + + def addelsestmt(self, stmt): + if self.elseb is None: + self.elseb = Block() + self.elseb.addstmt(stmt) + + def addelsestmts(self, stmts): + if self.elseb is None: + self.elseb = Block() + self.elseb.addstmts(stmts) + + +class StmtFor(Block): + def __init__(self, init=None, cond=None, update=None): + Block.__init__(self) + self.init = init + self.cond = cond + self.update = update + + +class StmtRangedFor(Block): + def __init__(self, var, iteree): + assert isinstance(var, ExprVar) + assert iteree + + Block.__init__(self) + self.var = var + self.iteree = iteree + + +class StmtSwitch(Block): + def __init__(self, expr): + Block.__init__(self) + self.expr = expr + self.nr_cases = 0 + + def addcase(self, case, block): + """NOTE: |case| is not checked for uniqueness""" + assert not isinstance(case, str) + assert ( + isinstance(block, StmtBreak) + or isinstance(block, StmtReturn) + or isinstance(block, StmtSwitch) + or isinstance(block, GroupNode) + or isinstance(block, VerbatimNode) + or ( + hasattr(block, "stmts") + and ( + isinstance(block.stmts[-1], StmtBreak) + or isinstance(block.stmts[-1], StmtReturn) + or isinstance(block.stmts[-1], GroupNode) + or isinstance(block.stmts[-1], VerbatimNode) + ) + ) + ) + self.addstmt(case) + self.addstmt(block) + self.nr_cases += 1 + + +class StmtBreak(Node): + def __init__(self): + Node.__init__(self) + + +class StmtExpr(Node): + def __init__(self, expr): + assert expr is not None + + Node.__init__(self) + self.expr = expr + + +class StmtReturn(Node): + def __init__(self, expr=None): + Node.__init__(self) + self.expr = expr + + +StmtReturn.TRUE = StmtReturn(ExprLiteral.TRUE) +StmtReturn.FALSE = StmtReturn(ExprLiteral.FALSE) diff --git a/ipc/ipdl/ipdl/cxx/cgen.py b/ipc/ipdl/ipdl/cxx/cgen.py new file mode 100644 index 0000000000..fecb90e97b --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/cgen.py @@ -0,0 +1,557 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import sys + +from ipdl.cgen import CodePrinter +from ipdl.cxx.ast import MethodSpec, TypeArray, Visitor, DestructorDecl + + +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): + if ws.indent: + self.printdent("") + self.write(ws.ws) + + def visitVerbatimNode(self, verb): + if verb.indent: + self.printdent("") + self.write(verb.text) + + def visitGroupNode(self, group): + offsetCols = self.indentCols * group.offset + self.col += offsetCols + for node in group.nodes: + node.accept(self) + self.col -= offsetCols + + def visitCppDirective(self, cd): + if cd.rest: + self.println("#%s %s" % (cd.directive, cd.rest)) + else: + self.println("#%s" % (cd.directive)) + + def visitNamespace(self, ns): + self.println("namespace " + ns.name + " {") + self.visitBlock(ns) + self.println("} // namespace " + ns.name) + + def visitType(self, t): + if t.const: + self.write("const ") + + self.write(t.name) + + if t.T is not None: + self.write("<") + if type(t.T) is list: + t.T[0].accept(self) + for tt in t.T[1:]: + self.write(", ") + tt.accept(self) + else: + t.T.accept(self) + self.write(">") + + if t.inner is not None: + self.write("::") + t.inner.accept(self) + + ts = "" + if t.ptr: + ts += "*" + elif t.ptrptr: + ts += "**" + elif t.ptrconstptr: + ts += "* const*" + + if t.ref: + ts += "&" + elif t.rvalref: + ts += "&&" + elif t.rightconst: + ts += " const" + + 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 visitTypeUnion(self, u): + self.write("union") + if u.name: + self.write(" " + u.name) + self.println(" {") + + self.indent() + for decl in u.components: + self.printdent() + decl.accept(self) + self.println(";") + self.dedent() + + self.printdent("}") + + def visitTypeFunction(self, fn): + self.write("std::function<") + fn.ret.accept(self) + self.write("(") + self.writeDeclList(fn.params) + self.write(")>") + + def visitTypedef(self, td): + if td.templateargs: + formals = ", ".join(["class " + T for T in td.templateargs]) + args = ", ".join(td.templateargs) + self.printdent("template<" + formals + "> using " + td.totypename + " = ") + td.fromtype.accept(self) + self.println("<" + args + ">;") + else: + self.printdent("typedef ") + td.fromtype.accept(self) + self.println(" " + td.totypename + ";") + + def visitUsing(self, us): + self.printdent("using ") + us.type.accept(self) + self.println(";") + + def visitForwardDecl(self, fd): + if fd.cls: + self.printdent("class ") + elif fd.struct: + self.printdent("struct ") + self.write(str(fd.pqname)) + self.println(";") + + def visitDecl(self, d): + # C-syntax arrays make code generation much more annoying + if isinstance(d.type, TypeArray): + d.type.basetype.accept(self) + else: + d.type.accept(self) + + if d.name: + self.write(" " + d.name) + + if isinstance(d.type, TypeArray): + self.write("[") + d.type.nmemb.accept(self) + self.write("]") + + def visitParam(self, p): + self.visitDecl(p) + if p.default is not None: + self.write(" = ") + p.default.accept(self) + + def visitClass(self, c): + if c.specializes is not None: + self.printdentln("template<>") + + if c.struct: + self.printdent("struct") + else: + self.printdent("class") + self.write(" " + c.name) + if c.final: + self.write(" final") + + if c.specializes is not None: + self.write(" <") + c.specializes.accept(self) + self.write(">") + + 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.type.accept(self) + + def visitFriendClassDecl(self, fcd): + self.printdentln("friend class " + fcd.friend + ";") + + def visitMethodDecl(self, md): + if md.T: + self.write("template<") + self.write("typename ") + md.T.accept(self) + self.println(">") + self.printdent() + + if md.warn_unused: + self.write("[[nodiscard]] ") + + if md.methodspec == MethodSpec.STATIC: + self.write("static ") + elif md.methodspec == MethodSpec.VIRTUAL or md.methodspec == MethodSpec.PURE: + self.write("virtual ") + + if md.ret: + if md.only_for_definition: + self.write("auto ") + else: + md.ret.accept(self) + self.println() + self.printdent() + + if md.cls is not None: + assert md.only_for_definition + + self.write(md.cls.name) + if md.cls.specializes is not None: + self.write("<") + md.cls.specializes.accept(self) + self.write(">") + self.write("::") + + if md.typeop is not None: + self.write("operator ") + md.typeop.accept(self) + else: + if isinstance(md, DestructorDecl): + self.write("~") + self.write(md.name) + + self.write("(") + self.writeDeclList(md.params) + self.write(")") + + if md.const: + self.write(" const") + if md.ret and md.only_for_definition: + self.write(" -> ") + md.ret.accept(self) + + if md.methodspec == MethodSpec.OVERRIDE: + self.write(" override") + elif md.methodspec == MethodSpec.PURE: + self.write(" = 0") + + def visitMethodDefn(self, md): + # Method specifiers are for decls, not defns. + assert md.decl.methodspec == MethodSpec.NONE + + self.printdent() + md.decl.accept(self) + self.println() + + self.printdentln("{") + self.indent() + self.visitBlock(md) + self.dedent() + self.printdentln("}") + + def visitConstructorDecl(self, cd): + if cd.explicit: + self.write("explicit ") + else: + self.write("MOZ_IMPLICIT ") + 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): + self.visitMethodDecl(dd) + + def visitDestructorDefn(self, dd): + self.printdent() + dd.decl.accept(self) + self.println() + + self.printdentln("{") + self.indent() + + self.visitBlock(dd) + + self.dedent() + self.printdentln("}") + + def visitExprLiteral(self, el): + self.write(str(el)) + + def visitExprVar(self, ev): + self.write(ev.name) + + def visitExprPrefixUnop(self, e): + self.write("(") + self.write(e.op) + self.write("(") + e.expr.accept(self) + self.write(")") + self.write(")") + + def visitExprCast(self, c): + if c.static: + pfx, sfx = "static_cast<", ">" + else: + assert c.const + pfx, sfx = "const_cast<", ">" + self.write(pfx) + c.type.accept(self) + self.write(sfx + "(") + c.expr.accept(self) + self.write(")") + + def visitExprBinary(self, e): + self.write("(") + e.left.accept(self) + self.write(") " + e.op + " (") + e.right.accept(self) + self.write(")") + + def visitExprConditional(self, c): + self.write("(") + c.cond.accept(self) + self.write(" ? ") + c.ife.accept(self) + self.write(" : ") + c.elsee.accept(self) + self.write(")") + + def visitExprSelect(self, es): + self.write("(") + es.obj.accept(self) + self.write(")") + self.write(es.op) + es.field.accept(self) + + def visitExprAssn(self, ea): + ea.lhs.accept(self) + self.write(" " + ea.op + " ") + 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 ") + if en.newargs is not None: + self.write("(") + self.writeExprList(en.newargs) + self.write(") ") + en.ctype.accept(self) + if en.args is not None: + self.write("(") + self.writeExprList(en.args) + self.write(")") + + def visitExprDelete(self, ed): + self.write("delete ") + ed.obj.accept(self) + + def visitExprLambda(self, l): + self.write("[") + ncaptures = len(l.captures) + for i, c in enumerate(l.captures): + c.accept(self) + if i != (ncaptures - 1): + self.write(", ") + self.write("](") + self.writeDeclList(l.params) + self.write(")") + if l.ret: + self.write(" -> ") + l.ret.accept(self) + self.println(" {") + self.indent() + self.visitBlock(l) + self.dedent() + self.printdent("}") + + 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.printdentln("}") + + def visitStmtFor(self, sf): + self.printdent("for (") + if sf.init is not None: + sf.init.accept(self) + self.write("; ") + if sf.cond is not None: + sf.cond.accept(self) + self.write("; ") + if sf.update is not None: + sf.update.accept(self) + self.println(") {") + + self.indent() + self.visitBlock(sf) + self.dedent() + self.printdentln("}") + + def visitStmtRangedFor(self, rf): + self.printdent("for (auto& ") + rf.var.accept(self) + self.write(" : ") + rf.iteree.accept(self) + self.println(") {") + + self.indent() + self.visitBlock(rf) + self.dedent() + self.printdentln("}") + + def visitStmtSwitch(self, sw): + self.printdent("switch (") + sw.expr.accept(self) + self.println(") {") + self.indent() + self.visitBlock(sw) + self.dedent() + self.printdentln("}") + + def visitStmtBreak(self, sb): + self.printdentln("break;") + + def visitStmtDecl(self, sd): + self.printdent() + sd.decl.accept(self) + if sd.initargs is not None: + self.write("{") + self.writeDeclList(sd.initargs) + self.write("}") + if sd.init is not None: + self.write(" = ") + sd.init.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() + for i, decl in enumerate(decls): + self.println() + self.printdent() + decl.accept(self) + if i != (ndecls - 1): + self.write(",") + 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(", ") diff --git a/ipc/ipdl/ipdl/cxx/code.py b/ipc/ipdl/ipdl/cxx/code.py new file mode 100644 index 0000000000..0b5019b623 --- /dev/null +++ b/ipc/ipdl/ipdl/cxx/code.py @@ -0,0 +1,187 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This module contains functionality for adding formatted, opaque "code" blocks +# into the IPDL ast. These code objects follow IPDL C++ ast patterns, and +# perform lowering in much the same way. + +# In general it is recommended to use these for blocks of code which would +# otherwise be specified by building a hardcoded IPDL-C++ AST, as users of this +# API are often easier to read than users of the AST APIs in these cases. + +import re +import math +import textwrap + +from ipdl.cxx.ast import Node, Whitespace, GroupNode, VerbatimNode + + +# ----------------------------------------------------------------------------- +# Public API. + + +def StmtCode(tmpl, **kwargs): + """Perform template substitution to build opaque C++ AST nodes. See the + module documentation for more information on the templating syntax. + + StmtCode nodes should be used where Stmt* nodes are used. They are placed + on their own line and indented.""" + return _code(tmpl, False, kwargs) + + +def ExprCode(tmpl, **kwargs): + """Perform template substitution to build opaque C++ AST nodes. See the + module documentation for more information on the templating syntax. + + ExprCode nodes should be used where Expr* nodes are used. They are placed + inline, and no trailing newline is added.""" + return _code(tmpl, True, kwargs) + + +def StmtVerbatim(text): + """Build an opaque C++ AST node which emits input text verbatim. + + StmtVerbatim nodes should be used where Stmt* nodes are used. They are placed + on their own line and indented.""" + return _verbatim(text, False) + + +def ExprVerbatim(text): + """Build an opaque C++ AST node which emits input text verbatim. + + ExprVerbatim nodes should be used where Expr* nodes are used. They are + placed inline, and no trailing newline is added.""" + return _verbatim(text, True) + + +# ----------------------------------------------------------------------------- +# Implementation + + +def _code(tmpl, inline, context): + # Remove common indentation, and strip the preceding newline from + # '''-quoting, because we usually don't want it. + if tmpl.startswith("\n"): + tmpl = tmpl[1:] + tmpl = textwrap.dedent(tmpl) + + # Process each line in turn, building up a list of nodes. + nodes = [] + for idx, line in enumerate(tmpl.splitlines()): + # Place newline tokens between lines in the input. + if idx > 0: + nodes.append(Whitespace.NL) + + # Don't indent the first line if `inline` is set. + skip_indent = inline and idx == 0 + nodes.append(_line(line.rstrip(), skip_indent, idx + 1, context)) + + # If we're inline, don't add the final trailing newline. + if not inline: + nodes.append(Whitespace.NL) + return GroupNode(nodes) + + +def _verbatim(text, inline): + # For simplicitly, _verbatim is implemented using the same logic as _code, + # but with '$' characters escaped. This ensures we only need to worry about + # a single, albeit complex, codepath. + return _code(text.replace("$", "$$"), inline, {}) + + +# Pattern used to identify substitutions. +_substPat = re.compile( + r""" + \$(?: + (?P<escaped>\$) | # '$$' is an escaped '$' + (?P<list>[*,])?{(?P<expr>[^}]+)} | # ${expr}, $*{expr}, or $,{expr} + (?P<invalid>) # For error reporting + ) + """, + re.IGNORECASE | re.VERBOSE, +) + + +def _line(raw, skip_indent, lineno, context): + assert "\n" not in raw + + # Determine the level of indentation used for this line + line = raw.lstrip() + offset = int(math.ceil((len(raw) - len(line)) / 4)) + + # If line starts with a directive, don't indent it. + if line.startswith("#"): + skip_indent = True + + column = 0 + children = [] + for match in _substPat.finditer(line): + if match.group("invalid") is not None: + raise ValueError("Invalid substitution on line %d" % lineno) + + # Any text from before the current entry should be written, and column + # advanced. + if match.start() > column: + before = line[column : match.start()] + children.append(VerbatimNode(before)) + column = match.end() + + # If we have an escaped group, emit a '$' node. + if match.group("escaped") is not None: + children.append(VerbatimNode("$")) + continue + + # At this point we should have an expression. + list_chr = match.group("list") + expr = match.group("expr") + assert expr is not None + + # Evaluate our expression in the context to get the values. + try: + values = eval(expr, context, {}) + except Exception as e: + msg = "%s in substitution on line %d" % (repr(e), lineno) + raise ValueError(msg) from e + + # If we aren't dealing with lists, wrap the result into a + # single-element list. + if list_chr is None: + values = [values] + + # Check if this substitution is inline, or the entire line. + inline = match.span() != (0, len(line)) + + for idx, value in enumerate(values): + # If we're using ',' as list mode, put a comma between each node. + if idx > 0 and list_chr == ",": + children.append(VerbatimNode(", ")) + + # If our value isn't a node, turn it into one. Verbatim should be + # inline unless indent isn't being skipped, and the match isn't + # inline. + if not isinstance(value, Node): + value = _verbatim(str(value), skip_indent or inline) + children.append(value) + + # If we were the entire line, indentation is handled by the added child + # nodes. Do this after the above loop such that created verbatims have + # the correct inline-ness. + if not inline: + skip_indent = True + + # Add any remaining text in the line. + if len(line) > column: + children.append(VerbatimNode(line[column:])) + + # If we have no children, just emit the empty string. This will become a + # blank line. + if len(children) == 0: + return VerbatimNode("") + + # Add the initial indent if we aren't skipping it. + if not skip_indent: + children.insert(0, VerbatimNode("", indent=True)) + + # Wrap ourselves into a group node with the correct indent offset + return GroupNode(children, offset=offset) |