muapitocstubs.py 13.6 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 200 201 202
        "MuMemOrd"        : "toMemoryOrder",
        "MuAtomicRMWOptr" : "toAtomicRMWOptr",
        "MuBinOptr"       : "toBinOptr",
        "MuCmpOptr"       : "toCmpOptr",
        "MuConvOptr"      : "toConvOptr",
Kunshan Wang's avatar
Kunshan Wang committed
203 204
        "MuCallConv"      : "toCallConv",
        "MuCommInst"      : "toCommInst",
205 206
        }

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

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

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

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

227 228 229
    if array_sz is not None:
        if type_is_handle_array(pt):
            ac = "readMuValueArray"
230 231
        elif type_is_node_array(pt):
            ac = "readMuIDArray"
232 233 234 235 236
        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))
237
        return "{}({}, {})".format(ac, rn, "_raw_"+array_sz)
238 239 240

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

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

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

254 255 256 257
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
258

259 260 261 262 263
    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
264 265 266 267
    pretty_name = "{}.{}".format(strname, name)

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

    stmts = []

271 272 273 274 275 276 277 278 279 280 281 282 283
    # 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))

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

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

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

    # convert parameters
295 296
    args_to_pass = []

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

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

304 305
        args_to_pass.append(pn)

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

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

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

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

    # make the call

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

324
    # return value
325

Kunshan Wang's avatar
Kunshan Wang committed
326 327 328 329 330 331 332 333 334 335 336
    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:
337
            assert(ret_ty == "MuBool" or jffi_setter == "setAddressReturn")
Kunshan Wang's avatar
Kunshan Wang committed
338 339 340 341 342 343 344
            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))


345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    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
361
    results.append("val stubsOf{} = new Array[Word]({})".format(name, len(ptrs)))
362 363 364 365
    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
366 367

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

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

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

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
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
418 419 420 421

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

422 423 424
    enums = generate_enums(ast)

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

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

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

    ast = muapiparser.parse_muapi(src_text)

    generated = generate_things(ast)

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

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