#!/usr/bin/python from __future__ import print_function import sys,os from functools import reduce import idl # typename -> ( ocaml_type, c_from_ocaml, ocaml_from_c ) builtins = { "bool": ("bool", "%(c)s = Bool_val(%(o)s)", "Val_bool(%(c)s)" ), "int": ("int", "%(c)s = Int_val(%(o)s)", "Val_int(%(c)s)" ), "char *": ("string option", "%(c)s = String_option_val(%(o)s)", "Val_string_option(%(c)s)"), "libxl_domid": ("domid", "%(c)s = Int_val(%(o)s)", "Val_int(%(c)s)" ), "libxl_devid": ("devid", "%(c)s = Int_val(%(o)s)", "Val_int(%(c)s)" ), "libxl_defbool": ("bool option", "%(c)s = Defbool_val(%(o)s)", "Val_defbool(%(c)s)" ), "libxl_uuid": ("int array", "Uuid_val(&%(c)s, %(o)s)", "Val_uuid(&%(c)s)"), "libxl_bitmap": ("bool array", "Bitmap_val(ctx, &%(c)s, %(o)s)", "Val_bitmap(&%(c)s)"), "libxl_key_value_list": ("(string * string) list", "libxl_key_value_list_val(&%(c)s, %(o)s)", "Val_key_value_list(&%(c)s)"), "libxl_string_list": ("string list", "libxl_string_list_val(&%(c)s, %(o)s)", "Val_string_list(&%(c)s)"), "libxl_mac": ("int array", "Mac_val(&%(c)s, %(o)s)", "Val_mac(&%(c)s)"), "libxl_hwcap": ("int32 array", None, "Val_hwcap(&%(c)s)"), "libxl_ms_vm_genid": ("int array", "Ms_vm_genid_val(&%(c)s, %(o)s)", "Val_ms_vm_genid(&%(c)s)"), # The following needs to be sorted out later "libxl_cpuid_policy_list": ("unit", "%(c)s = 0", "Val_unit"), } DEVICE_FUNCTIONS = [ ("add", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ("remove", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ("destroy", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ] DEVICE_LIST = [ ("list", ["ctx", "domid", "t list"]), ] functions = { # ( name , [type1,type2,....] ) "device_vfb": DEVICE_FUNCTIONS, "device_vkb": DEVICE_FUNCTIONS, "device_disk": DEVICE_FUNCTIONS + DEVICE_LIST + [ ("insert", ["ctx", "t", "domid", "?async:'a", "unit", "unit"]), ("of_vdev", ["ctx", "domid", "string", "t"]), ], "device_nic": DEVICE_FUNCTIONS + DEVICE_LIST + [ ("of_devid", ["ctx", "domid", "int", "t"]), ], "device_pci": DEVICE_FUNCTIONS + DEVICE_LIST + [ ("assignable_add", ["ctx", "t", "bool", "unit"]), ("assignable_remove", ["ctx", "t", "bool", "unit"]), ("assignable_list", ["ctx", "t list"]), ], "dominfo": [ ("list", ["ctx", "t list"]), ("get", ["ctx", "domid", "t"]), ], "physinfo": [ ("get", ["ctx", "t"]), ], "cputopology": [ ("get", ["ctx", "t array"]), ], "domain_sched_params": [ ("get", ["ctx", "domid", "t"]), ("set", ["ctx", "domid", "t", "unit"]), ], } def stub_fn_name(ty, name): return "stub_xl_%s_%s" % (ty.rawname,name) def ocaml_type_of(ty): if ty.rawname in ["domid","devid"]: return ty.rawname elif isinstance(ty,idl.UInt): if ty.width in [8, 16]: # handle as ints width = None elif ty.width in [32, 64]: width = ty.width else: raise NotImplementedError("Cannot handle %d-bit int" % ty.width) if width: return "int%d" % ty.width else: return "int" elif isinstance(ty,idl.Array): return "%s array" % ocaml_type_of(ty.elem_type) elif isinstance(ty,idl.Builtin): if ty.typename not in builtins: raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) typename,_,_ = builtins[ty.typename] if not typename: raise NotImplementedError("No typename for Builtin %s (%s)" % (ty.typename, type(ty))) return typename elif isinstance(ty,idl.KeyedUnion): return ty.union_name elif isinstance(ty,idl.Aggregate): if ty.rawname is None: return ty.anon_struct else: return ty.rawname.capitalize() + ".t" else: return ty.rawname ocaml_keywords = ['and', 'as', 'assert', 'begin', 'end', 'class', 'constraint', 'do', 'done', 'downto', 'else', 'if', 'end', 'exception', 'external', 'false', 'for', 'fun', 'function', 'functor', 'if', 'in', 'include', 'inherit', 'initializer', 'lazy', 'let', 'match', 'method', 'module', 'mutable', 'new', 'object', 'of', 'open', 'or', 'private', 'rec', 'sig', 'struct', 'then', 'to', 'true', 'try', 'type', 'val', 'virtual', 'when', 'while', 'with'] def munge_name(name): if name in ocaml_keywords: return "xl_" + name else: return name def ocaml_instance_of_field(f): if isinstance(f.type, idl.KeyedUnion): name = f.type.keyvar.name else: name = f.name return "%s : %s" % (munge_name(name), ocaml_type_of(f.type)) def gen_struct(ty, indent): s = "" for f in ty.fields: if f.type.private: continue x = ocaml_instance_of_field(f) x = x.replace("\n", "\n"+indent) s += indent + x + ";\n" return s def gen_ocaml_keyedunions(ty, interface, indent, parent = None): s = "" union_type = "" if ty.rawname is not None: # Non-anonymous types need no special handling pass elif isinstance(ty, idl.KeyedUnion): if parent is None: nparent = ty.keyvar.name else: nparent = parent + "_" + ty.keyvar.name for f in ty.fields: if f.type is None: continue if f.type.rawname is not None: continue if isinstance(f.type, idl.Struct) and not f.type.has_fields(): continue s += "\ntype %s_%s =\n" % (nparent,f.name) s += "{\n" s += gen_struct(f.type, indent + "\t") s += "}\n" name = "%s__union" % ty.keyvar.name s += "\n" s += "type %s = " % name u = [] for f in ty.fields: if f.type is None: u.append("%s" % (f.name.capitalize())) elif isinstance(f.type, idl.Struct): if f.type.rawname is not None: u.append("%s of %s.t" % (f.name.capitalize(), f.type.rawname.capitalize())) elif f.type.has_fields(): u.append("%s of %s_%s" % (f.name.capitalize(), nparent, f.name)) else: u.append("%s" % (f.name.capitalize())) else: raise NotImplementedError("Cannot handle KeyedUnion fields which are not Structs") s += " | ".join(u) + "\n" ty.union_name = name union_type = "?%s:%s" % (munge_name(nparent), ty.keyvar.type.rawname) if s == "": return None, None return s.replace("\n", "\n%s" % indent), union_type def gen_ocaml_anonstruct(ty, interface, indent, parent = None): s= "" if ty.rawname is not None: # Non-anonymous types need no special handling pass elif isinstance(ty, idl.Struct): name = "%s__anon" % parent s += "type %s = {\n" % name s += gen_struct(ty, indent) s += "}\n" ty.anon_struct = name if s == "": return None s = indent + s return s.replace("\n", "\n%s" % indent) def gen_ocaml_ml(ty, interface, indent=""): if interface: s = ("""(* %s interface *)\n""" % ty.typename) else: s = ("""(* %s implementation *)\n""" % ty.typename) if isinstance(ty, idl.Enumeration): s += "type %s = \n" % ty.rawname for v in ty.values: s += "\t | %s\n" % v.rawname if interface: s += "\nval string_of_%s : %s -> string\n" % (ty.rawname, ty.rawname) else: s += "\nlet string_of_%s = function\n" % ty.rawname for v in ty.values: s += '\t| %s -> "%s"\n' % (v.rawname, v.valuename) elif isinstance(ty, idl.Aggregate): s += "" if ty.typename is None: raise NotImplementedError("%s has no typename" % type(ty)) else: module_name = ty.rawname[0].upper() + ty.rawname[1:] if interface: s += "module %s : sig\n" % module_name else: s += "module %s = struct\n" % module_name # Handle KeyedUnions... union_types = [] for f in ty.fields: ku, union_type = gen_ocaml_keyedunions(f.type, interface, "\t") if ku is not None: s += ku s += "\n" if union_type is not None: union_types.append(union_type) # Handle anonymous structs... for f in ty.fields: anon = gen_ocaml_anonstruct(f.type, interface, "\t", f.name) if anon is not None: s += anon s += "\n" s += "\ttype t =\n" s += "\t{\n" s += gen_struct(ty, "\t\t") s += "\t}\n" if ty.init_fn is not None: union_args = "".join([u + " -> " for u in union_types]) if interface: s += "\tval default : ctx -> %sunit -> t\n" % union_args else: s += "\texternal default : ctx -> %sunit -> t = \"stub_libxl_%s_init\"\n" % (union_args, ty.rawname) if ty.rawname in functions: for name,args in functions[ty.rawname]: s += "\texternal %s : " % name s += " -> ".join(args) s += " = \"%s\"\n" % stub_fn_name(ty,name) s += "end\n" else: raise NotImplementedError("%s" % type(ty)) return s.replace("\n", "\n%s" % indent) def c_val(ty, c, o, indent="", parent = None): s = indent if isinstance(ty,idl.UInt): if ty.width in [8, 16]: # handle as ints width = None elif ty.width in [32, 64]: width = ty.width else: raise NotImplementedError("Cannot handle %d-bit int" % ty.width) if width: s += "%s = Int%d_val(%s);" % (c, width, o) else: s += "%s = Int_val(%s);" % (c, o) elif isinstance(ty,idl.Builtin): if ty.typename not in builtins: raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) _,fn,_ = builtins[ty.typename] if not fn: raise NotImplementedError("No c_val fn for Builtin %s (%s)" % (ty.typename, type(ty))) s += "%s;" % (fn % { "o": o, "c": c }) elif isinstance (ty,idl.Array): s += "{\n" s += "\tint i;\n" s += "\t%s = Wosize_val(%s);\n" % (parent + ty.lenvar.name, o) s += "\t%s = (%s) calloc(%s, sizeof(*%s));\n" % (c, ty.typename, parent + ty.lenvar.name, c) s += "\tfor(i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name) s += c_val(ty.elem_type, c+"[i]", "Field(%s, i)" % o, indent="\t\t", parent=parent) + "\n" s += "\t}\n" s += "}\n" elif isinstance(ty,idl.Enumeration) and (parent is None): n = 0 s += "switch(Int_val(%s)) {\n" % o for e in ty.values: s += " case %d: *%s = %s; break;\n" % (n, c, e.name) n += 1 s += " default: failwith_xl(ERROR_FAIL, \"cannot convert value to %s\"); break;\n" % ty.typename s += "}" elif isinstance(ty, idl.KeyedUnion): s += "{\n" s += "\tif(Is_long(%s)) {\n" % o n = 0 s += "\t\tswitch(Int_val(%s)) {\n" % o for f in ty.fields: if f.type is None or not f.type.has_fields(): s += "\t\t case %d: %s = %s; break;\n" % (n, parent + ty.keyvar.name, f.enumname) n += 1 s += "\t\t default: failwith_xl(ERROR_FAIL, \"variant handling bug %s%s (long)\"); break;\n" % (parent, ty.keyvar.name) s += "\t\t}\n" s += "\t} else {\n" s += "\t\t/* Is block... */\n" s += "\t\tswitch(Tag_val(%s)) {\n" % o n = 0 for f in ty.fields: if f.type is not None and f.type.has_fields(): if f.type.private: continue s += "\t\t case %d:\n" % (n) s += "\t\t %s = %s;\n" % (parent + ty.keyvar.name, f.enumname) (nparent,fexpr) = ty.member(c, f, False) s += "%s" % c_val(f.type, fexpr, "Field(%s, 0)" % o, parent=nparent, indent=indent+"\t\t ") s += "break;\n" n += 1 s += "\t\t default: failwith_xl(ERROR_FAIL, \"variant handling bug %s%s (block)\"); break;\n" % (parent, ty.keyvar.name) s += "\t\t}\n" s += "\t}\n" s += "}" elif isinstance(ty, idl.Aggregate) and (parent is None or ty.rawname is None): n = 0 for f in ty.fields: if f.type.private: continue (nparent,fexpr) = ty.member(c, f, ty.rawname is not None) s += "%s\n" % c_val(f.type, fexpr, "Field(%s, %d)" % (o,n), parent=nparent) n = n + 1 else: s += "%s_val(ctx, %s, %s);" % (ty.rawname, ty.pass_arg(c, parent is None, passby=idl.PASS_BY_REFERENCE), o) return s.replace("\n", "\n%s" % indent) def gen_c_val(ty, indent=""): s = "/* Convert caml value to %s */\n" % ty.rawname s += "static int %s_val (libxl_ctx *ctx, %s, value v)\n" % (ty.rawname, ty.make_arg("c_val", passby=idl.PASS_BY_REFERENCE)) s += "{\n" s += "\tCAMLparam1(v);\n" s += "\n" s += c_val(ty, "c_val", "v", indent="\t") + "\n" s += "\tCAMLreturn(0);\n" s += "}\n" return s.replace("\n", "\n%s" % indent) def ocaml_Val(ty, o, c, indent="", parent = None): s = indent if isinstance(ty,idl.UInt): if ty.width in [8, 16]: # handle as ints width = None elif ty.width in [32, 64]: width = ty.width else: raise NotImplementedError("Cannot handle %d-bit int" % ty.width) if width: s += "%s = caml_copy_int%d(%s);" % (o, width, c) else: s += "%s = Val_int(%s);" % (o, c) elif isinstance(ty,idl.Builtin): if ty.typename not in builtins: raise NotImplementedError("Unknown Builtin %s (%s)" % (ty.typename, type(ty))) _,_,fn = builtins[ty.typename] if not fn: raise NotImplementedError("No ocaml Val fn for Builtin %s (%s)" % (ty.typename, type(ty))) s += "%s = %s;" % (o, fn % { "c": c }) elif isinstance(ty, idl.Array): s += "{\n" s += "\t int i;\n" s += "\t CAMLlocal1(array_elem);\n" s += "\t %s = caml_alloc(%s,0);\n" % (o, parent + ty.lenvar.name) s += "\t for(i=0; i<%s; i++) {\n" % (parent + ty.lenvar.name) s += "\t %s\n" % ocaml_Val(ty.elem_type, "array_elem", c + "[i]", "", parent=parent) s += "\t Store_field(%s, i, array_elem);\n" % o s += "\t }\n" s += "\t}" elif isinstance(ty,idl.Enumeration) and (parent is None): n = 0 s += "switch(%s) {\n" % c for e in ty.values: s += " case %s: %s = Val_int(%d); break;\n" % (e.name, o, n) n += 1 s += " default: failwith_xl(ERROR_FAIL, \"cannot convert value from %s\"); break;\n" % ty.typename s += "}" elif isinstance(ty, idl.KeyedUnion): n = 0 m = 0 s += "switch(%s) {\n" % (parent + ty.keyvar.name) for f in ty.fields: s += "\t case %s:\n" % f.enumname if f.type is None: s += "\t /* %d: None */\n" % n s += "\t %s = Val_long(%d);\n" % (o,n) n += 1 elif not f.type.has_fields(): s += "\t /* %d: Long */\n" % n s += "\t %s = Val_long(%d);\n" % (o,n) n += 1 else: s += "\t /* %d: Block */\n" % m (nparent,fexpr) = ty.member(c, f, parent is None) s += "\t {\n" s += "\t\t CAMLlocal1(tmp);\n" s += "\t\t %s = caml_alloc(%d,%d);\n" % (o, 1, m) s += ocaml_Val(f.type, 'tmp', fexpr, indent="\t\t ", parent=nparent) s += "\n" s += "\t\t Store_field(%s, 0, tmp);\n" % o s += "\t }\n" m += 1 #s += "\t %s = caml_alloc(%d,%d);\n" % (o,len(f.type.fields),n) s += "\t break;\n" s += "\t default: failwith_xl(ERROR_FAIL, \"cannot convert value from %s\"); break;\n" % ty.typename s += "\t}" elif isinstance(ty,idl.Aggregate) and (parent is None or ty.rawname is None): s += "{\n" if ty.rawname is None: fn = "anon_field" else: fn = "%s_field" % ty.rawname s += "\tCAMLlocal1(%s);\n" % fn s += "\n" s += "\t%s = caml_alloc_tuple(%d);\n" % (o, len(ty.fields)) n = 0 for f in ty.fields: if f.type.private: continue (nparent,fexpr) = ty.member(c, f, parent is None) s += "\n" s += "\t%s\n" % ocaml_Val(f.type, fn, ty.pass_arg(fexpr, c), parent=nparent) s += "\tStore_field(%s, %d, %s);\n" % (o, n, fn) n = n + 1 s += "}" else: s += "%s = Val_%s(%s);" % (o, ty.rawname, ty.pass_arg(c, parent is None)) return s.replace("\n", "\n%s" % indent).rstrip(indent) def gen_Val_ocaml(ty, indent=""): s = "/* Convert %s to a caml value */\n" % ty.rawname s += "static value Val_%s (%s)\n" % (ty.rawname, ty.make_arg(ty.rawname+"_c")) s += "{\n" s += "\tCAMLparam0();\n" s += "\tCAMLlocal1(%s_ocaml);\n" % ty.rawname s += ocaml_Val(ty, "%s_ocaml" % ty.rawname, "%s_c" % ty.rawname, indent="\t") + "\n" s += "\tCAMLreturn(%s_ocaml);\n" % ty.rawname s += "}\n" return s.replace("\n", "\n%s" % indent) def gen_c_stub_prototype(ty, fns): s = "/* Stubs for %s */\n" % ty.rawname for name,args in fns: # For N args we return one value and take N-1 values as parameters s += "value %s(" % stub_fn_name(ty, name) s += ", ".join(["value v%d" % v for v in range(1,len(args))]) s += ");\n" return s def gen_c_default(ty): s = "/* Get the defaults for %s */\n" % ty.rawname # Handle KeyedUnions... union_types = [] for f in ty.fields: if isinstance(f.type, idl.KeyedUnion): union_types.append(f.type.keyvar) s += "value stub_libxl_%s_init(value ctx, %svalue unit)\n" % (ty.rawname, "".join(["value " + u.name + ", " for u in union_types])) s += "{\n" s += "\tCAMLparam%d(ctx, %sunit);\n" % (len(union_types) + 2, "".join([u.name + ", " for u in union_types])) s += "\tCAMLlocal1(val);\n" s += "\tlibxl_%s c_val;\n" % ty.rawname s += "\tlibxl_%s_init(&c_val);\n" % ty.rawname for u in union_types: s += "\tif (%s != Val_none) {\n" % u.name s += "\t\t%s c = 0;\n" % u.type.typename s += "\t\t%s_val(CTX, &c, Some_val(%s));\n" % (u.type.rawname, u.name) s += "\t\tlibxl_%s_init_%s(&c_val, c);\n" % (ty.rawname, u.name) s += "\t}\n" s += "\tval = Val_%s(&c_val);\n" % ty.rawname if ty.dispose_fn: s += "\tlibxl_%s_dispose(&c_val);\n" % ty.rawname s += "\tCAMLreturn(val);\n" s += "}\n" return s def gen_c_defaults(ty): s = gen_c_default(ty) return s def autogen_header(open_comment, close_comment): s = open_comment + " AUTO-GENERATED FILE DO NOT EDIT " + close_comment + "\n" s += open_comment + " autogenerated by \n" s += reduce(lambda x,y: x + " ", range(len(open_comment + " ")), "") s += "%s" % " ".join(sys.argv) s += "\n " + close_comment + "\n\n" return s if __name__ == '__main__': if len(sys.argv) < 4: print("Usage: genwrap.py ", file=sys.stderr) sys.exit(1) (_,types) = idl.parse(sys.argv[1]) # Do not generate these yet. blacklist = [ "cpupoolinfo", "vcpuinfo", ] for t in blacklist: if t not in [ty.rawname for ty in types]: print("unknown type %s in blacklist" % t) types = [ty for ty in types if not ty.rawname in blacklist] _ml = sys.argv[3] ml = open(_ml, 'w') ml.write(autogen_header("(*", "*)")) _mli = sys.argv[2] mli = open(_mli, 'w') mli.write(autogen_header("(*", "*)")) _cinc = sys.argv[4] cinc = open(_cinc, 'w') cinc.write(autogen_header("/*", "*/")) for ty in types: if ty.private: continue #sys.stdout.write(" TYPE %-20s " % ty.rawname) ml.write(gen_ocaml_ml(ty, False)) ml.write("\n") mli.write(gen_ocaml_ml(ty, True)) mli.write("\n") if ty.marshal_in(): cinc.write(gen_c_val(ty)) cinc.write("\n") cinc.write(gen_Val_ocaml(ty)) cinc.write("\n") if ty.rawname in functions: cinc.write(gen_c_stub_prototype(ty, functions[ty.rawname])) cinc.write("\n") if ty.init_fn is not None: cinc.write(gen_c_defaults(ty)) cinc.write("\n") #sys.stdout.write("\n") ml.write("(* END OF AUTO-GENERATED CODE *)\n") ml.close() mli.write("(* END OF AUTO-GENERATED CODE *)\n") mli.close() cinc.close()