muapitocstubs.py 13.7 KB
Newer Older
Kunshan Wang's avatar
Kunshan Wang committed
1 2 3
"""
Converts MuCtx methods in muapi.h to nativeClientSupport

4
USAGE: python3 muapitoncs.py
Kunshan Wang's avatar
Kunshan Wang committed
5

6
Code will be automatically generated to cStubs.scala
Kunshan Wang's avatar
Kunshan Wang committed
7 8 9 10

"""

import sys
11
import os, os.path
Kunshan Wang's avatar
Kunshan Wang committed
12
import re
13
import tempfile
14
from typing import Tuple
Kunshan Wang's avatar
Kunshan Wang committed
15

16
import muapiparser
Kunshan Wang's avatar
Kunshan Wang committed
17
import injecttools
18
from refimpl2injectablefiles import injectable_files, muapi_h_path
Kunshan Wang's avatar
Kunshan Wang committed
19

20 21
target_begin = '/// SCRIPT: GENERATED CODE BEGIN'
target_end   = '/// SCRIPT: GENERATED CODE END'
Kunshan Wang's avatar
Kunshan Wang committed
22

23
def inject_generated_code(parent, generated):
Kunshan Wang's avatar
Kunshan Wang committed
24 25 26 27 28
    return injecttools.inject_lines(parent, target_begin, target_end, generated)

# C types to Scala types, JFFI types and JFFI Buffer getters and setters

_primitive_types = {
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
        "void"      : ["Unit"   , "VOID"    , None         , None               ],
        "int"       : ["Int"    , "SINT"    , "getInt"     , "setIntReturn"     ],
        "long"      : ["Long"   , "SLONG"   , "getLong"    , "setLongReturn"    ],
        "int8_t"    : ["Byte"   , "SINT8"   , "getByte"    , "setByteReturn"    ],
        "uint8_t"   : ["Byte"   , "UINT8"   , "getByte"    , "setByteReturn"    ],
        "int16_t"   : ["Short"  , "SINT16"  , "getShort"   , "setShortReturn"   ],
        "uint16_t"  : ["Short"  , "UINT16"  , "getShort"   , "setShortReturn"   ],
        "int32_t"   : ["Int"    , "SINT32"  , "getInt"     , "setIntReturn"     ],
        "uint32_t"  : ["Int"    , "UINT32"  , "getInt"     , "setIntReturn"     ],
        "int64_t"   : ["Long"   , "SINT64"  , "getLong"    , "setLongReturn"    ],
        "uint64_t"  : ["Long"   , "UINT64"  , "getLong"    , "setLongReturn"    ],
        "intptr_t"  : ["Long"   , "POINTER" , "getAddress" , "setAddressReturn" ],
        "uintptr_t" : ["Long"   , "POINTER" , "getAddress" , "setAddressReturn" ],
        "float"     : ["Float"  , "FLOAT"   , "getFloat"   , "setFloatReturn"   ],
        "double"    : ["Double" , "DOUBLE"  , "getDouble"  , "setDoubleReturn"  ],
Kunshan Wang's avatar
Kunshan Wang committed
44 45
        }

46 47 48 49 50
_other_ptr_types = {
        # In the most recent muapi.h, these can be identified as explicit pointers.
        #"MuName", "MuCFP", "MuTrapHandler", "MuValueFreer"
        # Add more types here if the regexp cannot identify some pointer types.
        }
51

Kunshan Wang's avatar
Kunshan Wang committed
52
_self_getters = {
53
        "MuVM*": "getMicroVM",
Kunshan Wang's avatar
Kunshan Wang committed
54
        "MuCtx*": "getMuCtx",
55
        "MuIRBuilder*": "getMuIRBuilder",
Kunshan Wang's avatar
Kunshan Wang committed
56 57
        }

58
def type_is_explicit_ptr(ty):
Kunshan Wang's avatar
Kunshan Wang committed
59 60
    return ty.endswith("*")

61
r_handle_ty = re.compile(r'^Mu\w*(Value)$')
62 63 64 65

def type_is_handle(ty):
    return r_handle_ty.match(ty) is not None

66 67 68 69 70
r_node_ty = re.compile(r'^Mu\w*(Node|Clause)$')

def type_is_node(ty):
    return r_node_ty.match(ty) is not None

71
def type_is_ptr(ty):
72
    return type_is_explicit_ptr(ty) or type_is_handle(ty) or ty in _other_ptr_types
73 74

def type_is_handle_array(ty):
75
    return type_is_ptr(ty) and type_is_handle(ty[:-1])
76

77 78 79
def type_is_node_array(ty):
    return type_is_ptr(ty) and type_is_node(ty[:-1])

Kunshan Wang's avatar
Kunshan Wang committed
80 81 82 83
def to_jffi_ty(raw_type):
    if raw_type in _primitive_types:
        jty = _primitive_types[raw_type][1]
    elif type_is_ptr(raw_type):
84 85
        jty = "POINTER"
    else:
Kunshan Wang's avatar
Kunshan Wang committed
86
        raise ValueError("No JFFI JType: " + raw_type)
87 88 89

    return "JType." + jty

Kunshan Wang's avatar
Kunshan Wang committed
90 91 92 93
def to_jffi_getter(raw_type):
    if raw_type in _primitive_types:
        getter = _primitive_types[raw_type][2]
    elif type_is_ptr(raw_type):
94 95
        getter = "getAddress"
    else:
Kunshan Wang's avatar
Kunshan Wang committed
96 97 98 99 100 101 102 103 104 105 106
        raise ValueError("No JFFI Buffer getter: " + raw_type)

    return getter

def to_jffi_setter(raw_type):
    if raw_type in _primitive_types:
        getter = _primitive_types[raw_type][3]
    elif type_is_ptr(raw_type):
        getter = "setAddressReturn"
    else:
        raise ValueError("No JFFI Buffer getter: " + raw_type)
107 108 109

    return getter

Kunshan Wang's avatar
Kunshan Wang committed
110 111
_special_cases = {
        "id":             "ID",
112 113 114 115 116 117 118 119 120 121
        "sint8":          "SInt8",
        "uint8":          "UInt8",
        "sint16":         "SInt16",
        "uint16":         "UInt16",
        "sint32":         "SInt32",
        "uint32":         "UInt32",
        "sint64":         "SInt64",
        "uint64":         "UInt64",
        "uint64s":        "UInt64s",
        "fp":             "FP",
Kunshan Wang's avatar
Kunshan Wang committed
122 123 124 125 126 127 128 129
        "uptr":           "UPtr",
        "ufuncptr":       "UFuncPtr",
        "iref":           "IRef",
        "weakref":        "WeakRef",
        "funcref":        "FuncRef",
        "tagref64":       "TagRef64",
        "threadref":      "ThreadRef",
        "stackref":       "StackRef",
Kunshan Wang's avatar
Kunshan Wang committed
130
        "framecursorref": "FrameCursorRef",
Kunshan Wang's avatar
Kunshan Wang committed
131 132 133 134 135 136 137
        "irnoderef":      "IRNodeRef",
        "funcsig":        "FuncSig",
        "bb":             "BB",
        "binop":          "BinOp",
        "tailcall":       "TailCall",
        "extractvalue":   "ExtractValue",
        "insertvalue":    "InsertValue",
Kunshan Wang's avatar
Kunshan Wang committed
138
        "extractelement": "ExtractElement",
Kunshan Wang's avatar
Kunshan Wang committed
139 140 141 142 143 144 145 146
        "insertelement":  "InsertElement",
        "shufflevector":  "ShuffleVector",
        "newhybrid":      "NewHybrid",
        "allocahybrid":   "AllocaHybrid",
        "getiref":        "GetIRef",
        "getfieldiref":   "GetFieldIRef",
        "getelemiref":    "GetElemIRef",
        "shiftiref":      "ShiftIRef",
Kunshan Wang's avatar
Kunshan Wang committed
147
        "getvarpartiref": "GetVarPartIRef",
Kunshan Wang's avatar
Kunshan Wang committed
148 149 150 151 152 153 154 155 156
        "cmpxchg":        "CmpXchg",
        "atomicrmw":      "AtomicRMW",
        "watchpoint":     "WatchPoint",
        "wpbranch":       "WPBranch",
        "ccall":          "CCall",
        "newthread":      "NewThread",
        "newstack":       "NewStack",
        "swapstack":      "SwapStack",
        "comminst":       "CommInst",
157
        "ir":             "IR",
Kunshan Wang's avatar
Kunshan Wang committed
158
        "irbuilderref":   "IRBuilderRef",
Kunshan Wang's avatar
Kunshan Wang committed
159 160 161 162 163 164
        }

def toCamelCase(name):
    ins = name.split("_")
    outs = [ins[0]]
    for inn in ins[1:]:
165 166
        if inn in _special_cases:
            outs.append(_special_cases[inn])
Kunshan Wang's avatar
Kunshan Wang committed
167 168 169 170 171
        else:
            outs.append(inn[0].upper()+inn[1:])

    return "".join(outs)

172 173 174 175
def to_basic_type(typedefs, name):
    while name in typedefs:
        name = typedefs[name]
    return name
Kunshan Wang's avatar
Kunshan Wang committed
176

177
_no_conversion = {
Kunshan Wang's avatar
Kunshan Wang committed
178
        # "MuID",          # It may be optional, in which case it needs conversion.
179 180 181 182
        "MuTrapHandler", # It is a function pointer. Handle in Scala.
        "MuCPtr",        # Intended to be raw pointer. Passed directly.
        "MuCFP",         # ditto
        "MuWPID",        # Just Int
Kunshan Wang's avatar
Kunshan Wang committed
183
        # "MuCommInst",    # same as MuID
184 185 186
        }

_array_converters = {
187
        "char*"     : "readCharArray",
188 189
        "uint64_t*" : "readLongArray",
        "MuFlag*"   : "readFlagArray",
Kunshan Wang's avatar
Kunshan Wang committed
190
        "MuID*"     : "readIntArray",
191
        "MuCString*": "readCStringArray",
192 193 194
        }

_special_converters = {
195
        "MuBool"          : "intToBoolean",
196
        "MuName"          : "readCString",
Kunshan Wang's avatar
Kunshan Wang committed
197
        "MuCString"       : "readCString",
198 199
        "MuMemOrd"        : "toMemoryOrder",
        "MuAtomicRMWOptr" : "toAtomicRMWOptr",
200
        "MuBinOpStatus"   : "toBinOpStatus",
201 202 203
        "MuBinOptr"       : "toBinOptr",
        "MuCmpOptr"       : "toCmpOptr",
        "MuConvOptr"      : "toConvOptr",
Kunshan Wang's avatar
Kunshan Wang committed
204 205
        "MuCallConv"      : "toCallConv",
        "MuCommInst"      : "toCommInst",
206 207
        }

Kunshan Wang's avatar
Kunshan Wang committed
208
_special_return_converters = {
209
        "MuBool" : "booleanToInt",
Kunshan Wang's avatar
Kunshan Wang committed
210 211
        "MuName" : "exposeString",
        "MuVM*"  : "exposeMicroVM",
212 213
        "MuCtx*" : "exposeMuCtx",
        "MuIRBuilder*" : "exposeMuIRBuilder",
Kunshan Wang's avatar
Kunshan Wang committed
214 215
        }

216
def param_converter(pn, pt, rn, rt, is_optional, array_sz, is_out):
217 218 219
    if pt == "void":
        raise ValueError("Parameter cannot be void. Param name: {}".format(pn))

220
    if pt in _primitive_types or pt in _no_conversion or is_out:
221 222
        return rn   # does not need conversion

Kunshan Wang's avatar
Kunshan Wang committed
223 224 225
    if type_is_node(pt) or pt == "MuID":
        if is_optional:
            return "readMuIDOptional({})".format(rn)
226 227
        return rn   # does not need conversion

228 229 230
    if array_sz is not None:
        if type_is_handle_array(pt):
            ac = "readMuValueArray"
231 232
        elif type_is_node_array(pt):
            ac = "readMuIDArray"
233 234 235 236 237
        elif pt in _array_converters:
            ac = _array_converters[pt]
        else:
            raise ValueError("I don't know how to convert array {}. "
                    "Param name: {}, array size: {}".format(pt, pn, array_sz))
238
        return "{}({}, {})".format(ac, rn, "_raw_"+array_sz)
239 240 241

    if type_is_handle(pt):
        if is_optional:
Kunshan Wang's avatar
Kunshan Wang committed
242
            return "getMuValueNullable({}).asInstanceOf[Option[{}]]".format(rn, pt)
243
        else:
Kunshan Wang's avatar
Kunshan Wang committed
244
            return "getMuValueNotNull({}).asInstanceOf[{}]".format(rn, pt)
245 246

    if pt in _special_converters:
Kunshan Wang's avatar
Kunshan Wang committed
247 248 249 250
        converter = _special_converters[pt]
        if is_optional:
            converter = converter + "Optional"
        return "{}({})".format(converter, rn)
251 252 253 254

    raise ValueError("I don't know how to convert {}. Param name: {}".format(
        pt, pn))

255 256 257 258
def generate_method(typedefs, strname, meth) -> Tuple[str, str]:
    name    = meth['name']
    params  = meth['params']
    ret_ty  = meth['ret_ty']
Kunshan Wang's avatar
Kunshan Wang committed
259

260 261 262 263 264
    valname = strname.upper() + "__" + name.upper()

    jffi_retty = to_jffi_ty(to_basic_type(typedefs, ret_ty))
    jffi_paramtys = [to_jffi_ty(to_basic_type(typedefs, p["type"])) for p in params]

Kunshan Wang's avatar
Kunshan Wang committed
265 266 267 268
    pretty_name = "{}.{}".format(strname, name)

    header = 'val {} = exposedMethod("{}", {}, Array({})) {{ _jffiBuffer =>'.format(
            valname, pretty_name, jffi_retty, ", ".join(jffi_paramtys))
269 270 271

    stmts = []

272 273 274 275 276 277 278 279 280 281 282 283 284
    # get raw parameters
    for i in range(len(params)):
        param = params[i]
        pn = param['name']
        pt = param['type']
        rt = to_basic_type(typedefs, pt) # raw type
        jffi_getter = to_jffi_getter(rt)

        rn = "_raw_" + pn # raw name

        stmts.append("val {} = _jffiBuffer.{}({})".format(rn,
            jffi_getter, i))

285 286 287
    self_param_name = params[0]["name"]
    self_param_type = params[0]["type"]

288
    # get the self object (MuVM or MuCtx)
289 290

    stmts.append("val {} = {}({})".format(
291 292 293 294 295
        self_param_name,
        _self_getters[self_param_type],
        "_raw_"+self_param_name))

    # convert parameters
296 297
    args_to_pass = []

298 299 300 301
    for i in range(1, len(params)):
        param = params[i]
        pn = param['name']

302
        if param.get("is_sz_param", False):
303 304
            continue    # Array sizes don't need to be passed explicitly.

305 306
        args_to_pass.append(pn)

307 308 309 310
        pt = param['type']
        rn = "_raw_" + pn
        rt = to_basic_type(typedefs, pt)

311 312 313
        array_sz = param.get("array_sz_param", None)
        is_optional = param.get("is_optional", False)
        is_out = param.get("is_out", False)
314

315
        pc = param_converter(pn, pt, rn, rt, is_optional, array_sz, is_out)
316 317 318 319 320

        stmts.append("val {} = {}".format(pn, pc))

    # make the call

321 322 323 324
    camelName = toCamelCase(name)
    stmts.append("val _RV = {}.{}({})".format(
        self_param_name, camelName, ", ".join(args_to_pass)))

325
    # return value
326

Kunshan Wang's avatar
Kunshan Wang committed
327 328 329 330 331 332 333 334 335 336 337
    if ret_ty != "void":
        raw_ret_ty = to_basic_type(typedefs, ret_ty)
        jffi_setter = to_jffi_setter(raw_ret_ty)

        if type_is_handle(ret_ty):
            assert(strname == "MuCtx")
            assert(jffi_setter == "setAddressReturn")
            stmts.append("val _RV_FAK = exposeMuValue({}, _RV)".format(
                self_param_name))
            stmts.append("_jffiBuffer.{}(_RV_FAK)".format(jffi_setter))
        elif ret_ty in _special_return_converters:
338
            assert(ret_ty == "MuBool" or jffi_setter == "setAddressReturn")
Kunshan Wang's avatar
Kunshan Wang committed
339 340 341 342 343 344 345
            stmts.append("val _RV_FAK = {}(_RV)".format(
                _special_return_converters[ret_ty]))
            stmts.append("_jffiBuffer.{}(_RV_FAK)".format(jffi_setter))
        else:
            stmts.append("_jffiBuffer.{}(_RV)".format(jffi_setter))


346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    footer = "}"

    return (valname, "\n".join([header] + stmts + [footer]))

def generate_stubs_for_struct(typedefs, st) -> str:
    name    = st["name"]
    methods = st["methods"]

    results = []
    ptrs    = []

    for meth in methods:
        ptrname, code = generate_method(typedefs, name, meth)
        ptrs.append(ptrname)
        results.append(code)

Kunshan Wang's avatar
Kunshan Wang committed
362
    results.append("val stubsOf{} = new Array[Word]({})".format(name, len(ptrs)))
363 364 365 366
    for i,ptr in enumerate(ptrs):
        results.append("stubsOf{}({}) = {}.address".format(name, i, ptr))

    return "\n".join(results)
Kunshan Wang's avatar
Kunshan Wang committed
367 368

def generate_stubs(ast):
369 370
    struct_codes = []

Kunshan Wang's avatar
Kunshan Wang committed
371
    for st in ast["structs"]:
372 373
        code = generate_stubs_for_struct(ast["typedefs"], st)
        struct_codes.append(code)
Kunshan Wang's avatar
Kunshan Wang committed
374

375
    return "\n".join(struct_codes)
Kunshan Wang's avatar
Kunshan Wang committed
376

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
def generate_enums(ast):
    vals = []

    for enum in ast['enums']:
        for d in enum['defs']:
            vals.append("val {} = {}".format(d['name'], d['value']))

    return "\n".join(vals)

_enum_types_to_generate_converters = [
        ("MuBinOptr",       "BinOptr",       'MU_BINOP_'),
        ("MuCmpOptr",       "CmpOptr",       'MU_CMP_'),
        ("MuConvOptr",      "ConvOptr",      'MU_CONV_'),
        ("MuMemOrd",        "MemoryOrder",   'MU_ORD_'),
        ("MuAtomicRMWOptr", "AtomicRMWOptr", 'MU_ARMW_'),
        ]

def generate_enum_converters(ast):
    enums = ast['enums']
    edict = {}

    for e in enums:
        edict[e['name']] = e['defs']

    lines = []

    for cty, sty, prefix in _enum_types_to_generate_converters:
        func_name = "to"+sty
        lines.append("def {}(cval: {}): {}.Value = cval match {{".format(
            func_name, cty, sty))

        defs = edict[cty]
        for d in defs:
            dn = d['name']
            dv = d['value']
            assert(dn.startswith(prefix))
            sn = dn[len(prefix):]
            lines.append("  case {} => {}.{}".format(dv, sty, sn))

        lines.append("}")

    return "\n".join(lines)
Kunshan Wang's avatar
Kunshan Wang committed
419 420 421 422

def generate_things(ast):
    stubs = generate_stubs(ast)

423 424 425
    enums = generate_enums(ast)

    enum_convs = generate_enum_converters(ast)
Kunshan Wang's avatar
Kunshan Wang committed
426

427
    return "\n".join([stubs, enums, enum_convs])
Kunshan Wang's avatar
Kunshan Wang committed
428 429

def main():
430
    with open(muapi_h_path) as f:
Kunshan Wang's avatar
Kunshan Wang committed
431 432 433 434 435 436
        src_text = f.read()

    ast = muapiparser.parse_muapi(src_text)

    generated = generate_things(ast)

437 438 439
    injectable_files["cStubs.scala"].inject_many({
        "STUBS": generated,
        })
Kunshan Wang's avatar
Kunshan Wang committed
440

Kunshan Wang's avatar
Kunshan Wang committed
441 442
if __name__=='__main__':
    main()