pmImgCreator.py

Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 # This file is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall.
00004 #
00005 # This file is part of the Python-on-a-Chip program.
00006 # Python-on-a-Chip is free software: you can redistribute it and/or modify
00007 # it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1.
00008 #
00009 # Python-on-a-Chip is distributed in the hope that it will be useful,
00010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00012 # A copy of the GNU LESSER GENERAL PUBLIC LICENSE Version 2.1
00013 # is seen in the file COPYING in this directory.
00014 
00015 """
00016 PyMite Image Creator
00017 ====================
00018 
00019 Converts Python source files to a PyMite code image library.
00020 Performs code filtering to ensure it will run in PyMite.
00021 Formats the image as a raw binary file or a C file
00022 containing a byte array.
00023 
00024 16- and 32-bit values are in LITTLE ENDIAN order.
00025 This matches both Python and the AVR compiler's access to EEPROM.
00026 
00027 The order of the images in the output is undetermined.
00028 
00029 If the Python source contains a native code declaration
00030 and '--native-file=filename" is specified, the native code
00031 is formatted as C functions and an array of functions and output
00032 to the given filename.  When native functions are present, the user
00033 should specify where the native functions should be placed-- in the
00034 standard library or the user library--using the argument -s or -u,
00035 respectively.
00036 """
00037 
00038 ## @file
00039 #  @copybrief pmImgCreator
00040 
00041 ## @package pmImgCreator
00042 #  @brief PyMite Image Creator
00043 #
00044 #  See the source docstring for more details.
00045 
00046 __usage__ = """USAGE:
00047     pmImgCreator.py -f pmfeaturesfilename [-b|c] [-s|u] [OPTIONS] -o imgfilename file0.py [files...]
00048 
00049     -f <fn> Specify the file containing the PM_FEATURES dict to use
00050     -b      Generates a raw binary file of the image
00051     -c      Generates a C file of the image (default)
00052 
00053     -s      Place native functions in the PyMite standard library (default)
00054     -u      Place native functions in the user library
00055 
00056     OPTIONS:
00057     --native-file=filename  If specified, pmImgCreator will write a C source
00058                             file with native functions from the python files.
00059     --memspace=ram|flash    Sets the memory space in which the image will be
00060                             placed (default is "ram")
00061     """
00062 
00063 
00064 import exceptions, string, sys, types, dis, os, time, getopt, struct, types
00065 
00066 
00067 ################################################################
00068 # CONSTANTS
00069 ################################################################
00070 
00071 # Exit error codes (from /usr/include/sysexits.h)
00072 EX_USAGE = 64
00073 
00074 # remove documentation string from const pool
00075 REMOVE_DOC_STR = False
00076 
00077 # Pm obj descriptor type constants
00078 # Must match PmType_e in pm.h
00079 OBJ_TYPE_NON = 0x00     # None
00080 OBJ_TYPE_INT = 0x01     # Signed integer
00081 OBJ_TYPE_FLT = 0x02     # Floating point 32b
00082 OBJ_TYPE_STR = 0x03     # String
00083 OBJ_TYPE_TUP = 0x04     # Tuple (static sequence)
00084 OBJ_TYPE_COB = 0x05     # Code obj
00085 OBJ_TYPE_MOD = 0x06     # Module obj
00086 OBJ_TYPE_CLO = 0x07     # Class obj
00087 OBJ_TYPE_FXN = 0x08     # Funtion obj
00088 OBJ_TYPE_CLI = 0x09     # Class instance
00089 OBJ_TYPE_CIM = 0x0A     # Code image
00090 OBJ_TYPE_NIM = 0x0B     # Native func img
00091 OBJ_TYPE_NOB = 0x0C     # Native func obj
00092 # All types after this never appear in an image
00093 
00094 # Number of bytes in a native image (constant)
00095 NATIVE_IMG_SIZE = 4
00096 
00097 # Maximum number of objs in a tuple
00098 MAX_TUPLE_LEN = 253
00099 
00100 # Maximum number of chars in a string (XXX bytes vs UTF-8 chars?)
00101 MAX_STRING_LEN = 999
00102 
00103 # Maximum number of chars in a code img
00104 MAX_IMG_LEN = 32767
00105 
00106 # Masks for co_flags (from Python's code.h)
00107 CO_OPTIMIZED = 0x0001
00108 CO_NEWLOCALS = 0x0002
00109 CO_VARARGS = 0x0004
00110 CO_VARKEYWORDS = 0x0008
00111 CO_NESTED = 0x0010
00112 CO_GENERATOR = 0x0020
00113 CO_NOFREE = 0x0040
00114 
00115 # String used to ID a native method
00116 NATIVE_INDICATOR = "__NATIVE__"
00117 NATIVE_INDICATOR_LENGTH = len(NATIVE_INDICATOR)
00118 
00119 # String name of function table variable
00120 NATIVE_TABLE_NAME = {"std": "std_nat_fxn_table",
00121                      "usr": "usr_nat_fxn_table"
00122                     }
00123 
00124 # String name to prefix all native functions
00125 NATIVE_FUNC_PREFIX = "nat_"
00126 
00127 # Maximum number of locals a native func can have (frame.h)
00128 NATIVE_MAX_NUM_LOCALS = 8
00129 
00130 # Issue #51: In Python 2.5, the module identifier changed from '?' to '<module>'
00131 if float(sys.version[:3]) < 2.5:
00132     MODULE_IDENTIFIER = "?"
00133 else:
00134     MODULE_IDENTIFIER = "<module>"
00135 
00136 # Old #152: Byte to append after the last image in the list
00137 IMG_LIST_TERMINATOR = "\xFF"
00138 
00139 
00140 ################################################################
00141 # GLOBALS
00142 ################################################################
00143 
00144 # Number of bytes from top of code img to start of consts:
00145 # type, sizelo, sizehi, co_argcount, co_flags, co_stacksize, co_nlocals
00146 CO_IMG_FIXEDPART_SIZE = 7
00147 
00148 # PyMite's unimplemented bytecodes (from Python 2.0 through 2.5)
00149 UNIMPLEMENTED_BCODES = [
00150     "SLICE+1", "SLICE+2", "SLICE+3",
00151     "STORE_SLICE+0", "STORE_SLICE+1", "STORE_SLICE+2", "STORE_SLICE+3",
00152     "DELETE_SLICE+0", "DELETE_SLICE+1", "DELETE_SLICE+2", "DELETE_SLICE+3",
00153     "PRINT_ITEM_TO", "PRINT_NEWLINE_TO",
00154     "WITH_CLEANUP",
00155     "EXEC_STMT",
00156     "END_FINALLY",
00157     "SETUP_EXCEPT", "SETUP_FINALLY",
00158     "BUILD_SLICE",
00159     "CALL_FUNCTION_VAR", "CALL_FUNCTION_KW", "CALL_FUNCTION_VAR_KW",
00160     "EXTENDED_ARG",
00161     ]
00162 
00163 
00164 ################################################################
00165 # CLASS
00166 ################################################################
00167 
00168 class PmImgCreator:
00169     def __init__(self, pmfeatures_filename):
00170 
00171         # Issue #88: Consolidate HAVE_* platform feature definitions
00172         # Execute the pmfeatures file to get the features dict
00173         locs = {}
00174         execfile(pmfeatures_filename, {}, locs)
00175         global PM_FEATURES
00176         PM_FEATURES = locs['PM_FEATURES']
00177         assert type(PM_FEATURES) == dict
00178 
00179         # Modify some globals based on the platform features
00180         global CO_IMG_FIXEDPART_SIZE
00181         global UNIMPLEMENTED_BCODES
00182 
00183         if PM_FEATURES["HAVE_CLOSURES"]:
00184             CO_IMG_FIXEDPART_SIZE += 1  # [co_nfreevars]
00185 
00186         if PM_FEATURES["HAVE_DEBUG_INFO"]:
00187             CO_IMG_FIXEDPART_SIZE += 2  # [co_firstlineno]
00188 
00189         if not PM_FEATURES["HAVE_DEL"]:
00190             UNIMPLEMENTED_BCODES.extend([
00191                 "DELETE_SUBSCR",
00192                 "DELETE_NAME",
00193                 "DELETE_GLOBAL",
00194                 "DELETE_ATTR",
00195                 "DELETE_FAST",
00196                 ])
00197 
00198         if not PM_FEATURES["HAVE_IMPORTS"]:
00199             UNIMPLEMENTED_BCODES.extend([
00200                 "IMPORT_STAR",
00201                 "IMPORT_FROM",
00202                 ])
00203 
00204         if not PM_FEATURES["HAVE_ASSERT"]:
00205             UNIMPLEMENTED_BCODES.extend([
00206                 "RAISE_VARARGS",
00207                 ])
00208 
00209         if not PM_FEATURES["HAVE_CLASSES"]:
00210             UNIMPLEMENTED_BCODES.extend([
00211                 "BUILD_CLASS",
00212                 ])
00213 
00214         # Issue #7: Add support for the yield keyword
00215         if not PM_FEATURES["HAVE_GENERATORS"]:
00216             UNIMPLEMENTED_BCODES.extend([
00217                 "YIELD_VALUE",
00218                 ])
00219 
00220         # Issue #44: Add support for the backtick operation (UNARY_CONVERT)
00221         if not PM_FEATURES["HAVE_BACKTICK"]:
00222             UNIMPLEMENTED_BCODES.extend([
00223                 "UNARY_CONVERT",
00224                 ])
00225 
00226         # Issue #13: Add support for Python 2.6 bytecodes.
00227         # The *_TRUE_DIVIDE bytecodes require support for float type
00228         if not PM_FEATURES["HAVE_FLOAT"]:
00229             UNIMPLEMENTED_BCODES.extend([
00230                 "BINARY_TRUE_DIVIDE",
00231                 "INPLACE_TRUE_DIVIDE",
00232                 ])
00233 
00234         # Issue #56: Add support for decorators
00235         if not PM_FEATURES["HAVE_CLOSURES"]:
00236             UNIMPLEMENTED_BCODES.extend([
00237                 "MAKE_CLOSURE",
00238                 "LOAD_CLOSURE",
00239                 "LOAD_DEREF",
00240                 "STORE_DEREF",
00241                 ])
00242 
00243 
00244         self.formatFromExt = {".c": self.format_img_as_c,
00245                               ".bin": self.format_img_as_bin,
00246                              }
00247 
00248         # bcode to mnemonic conversion (sparse list of strings)
00249         bcodes = dis.opname[:]
00250 
00251         # remove invalid bcodes
00252         for i in range(len(bcodes)):
00253             if bcodes[i][0] == '<':
00254                 bcodes[i] = None
00255 
00256         # remove unimplmented bcodes
00257         for bcname in UNIMPLEMENTED_BCODES:
00258             if bcname in bcodes:
00259                 i = bcodes.index(bcname)
00260                 bcodes[i] = None
00261 
00262         # set class variables
00263         self.bcodes = bcodes
00264 
00265         # function renames
00266         self._U8_to_str = chr
00267         self._str_to_U8 = ord
00268 
00269 
00270     def set_options(self,
00271                     outfn,
00272                     imgtype,
00273                     imgtarget,
00274                     memspace,
00275                     nativeFilename,
00276                     infiles,
00277                    ):
00278         self.outfn = outfn
00279         self.imgtype = imgtype
00280         self.imgtarget = imgtarget
00281         self.memspace = memspace
00282         self.nativeFilename = nativeFilename
00283         self.infiles = infiles
00284 
00285 ################################################################
00286 # CONVERSION FUNCTIONS
00287 ################################################################
00288 
00289     def convert_files(self,):
00290         """Attempts to convert all source files.
00291         Creates a dict whose keys are the filenames
00292         and values are the code object string.
00293         """
00294         # init image dict
00295         imgs = {"imgs": [], "fns": []}
00296 
00297         # init module table and native table
00298         self.nativemods = []
00299         self.nativetable = []
00300 
00301         # if creating usr lib, create placeholder in 0th index
00302         if self.imgtarget == "usr":
00303             self.nativetable.append((NATIVE_FUNC_PREFIX + "placeholder_func",
00304                                     "\n    /*\n"
00305                                     "     * Use placeholder because an index \n"
00306                                     "     * value of zero denotes the stdlib.\n"
00307                                     "     * This function should not be called.\n"
00308                                     "     */\n"
00309                                     "    PmReturn_t retval;\n"
00310                                     "    PM_RAISE(retval, PM_RET_EX_SYS);\n"
00311                                     "    return retval;\n"
00312                                    ))
00313             self.nfcount = 1
00314         else:
00315             self.nfcount = 0
00316 
00317         # for each src file, convert and format
00318         for fn in self.infiles:
00319 
00320             # try to compile and convert the file
00321             co = compile(open(fn).read(), fn, 'exec')
00322             imgs["fns"].append(fn)
00323             imgs["imgs"].append(self.co_to_str(co))
00324 
00325         # Append null terminator to list of images
00326         imgs["fns"].append("img-list-terminator")
00327         imgs["imgs"].append(IMG_LIST_TERMINATOR)
00328 
00329         self.imgDict = imgs
00330         return
00331 
00332 
00333     def _str_to_U16(self, s):
00334         """Convert two bytes from a sequence to a 16-bit word.
00335 
00336         The bytes are expected in little endian order.
00337         LSB first.
00338         """
00339 
00340         return self._str_to_U8(s[0]) | (self._str_to_U8(s[1]) << 8)
00341 
00342 
00343     def _U16_to_str(self, w):
00344         """Convert the 16-bit word, w, to a string of two bytes.
00345 
00346         The 2 byte string is in little endian order.
00347         DOES NOT INSERT TYPE BYTE.
00348         """
00349 
00350         return self._U8_to_str(w & 0xff) + \
00351                self._U8_to_str((w >> 8) & 0xff)
00352 
00353 
00354     def _float_to_str(self, f):
00355         """Convert the float object, f, to a string of four bytes
00356         in little-endian order.
00357         """
00358         return struct.pack("<f", f)
00359 
00360 
00361     def _seq_to_str(self, seq):
00362         """Convert a Python sequence to a PyMite image.
00363 
00364         The sequence is converted to a tuple of objects.
00365         This handles both co_consts and co_names.
00366         This is recursive to handle tuples in the const pool.
00367         Return string shows type in the leading byte.
00368         """
00369 
00370         # OPT
00371         _U8_to_str = self._U8_to_str
00372 
00373         # ensure tuple fits within limits
00374         assert len(seq) <= MAX_TUPLE_LEN
00375 
00376         # image string init with tuple marker and num elements
00377         imgstr = _U8_to_str(OBJ_TYPE_TUP) + \
00378                  _U8_to_str(len(seq))
00379 
00380         # iterate through the sequence of objects
00381         for i in range(len(seq)):
00382             obj = seq[i]
00383             objtype = type(obj)
00384 
00385             # if it is a string
00386             if objtype == types.StringType:
00387                 # ensure string is not too long
00388                 assert len(obj) <= MAX_STRING_LEN
00389                 # marker, string length, string itself
00390                 imgstr += _U8_to_str(OBJ_TYPE_STR) + \
00391                           self._U16_to_str(len(obj)) + obj
00392 
00393             # if it is an integer
00394             elif objtype == types.IntType:
00395                 # marker, int (little endian)
00396                 imgstr += _U8_to_str(OBJ_TYPE_INT) + \
00397                           _U8_to_str(obj & 0xff) + \
00398                           _U8_to_str((obj >>  8) & 0xff) + \
00399                           _U8_to_str((obj >> 16) & 0xff) + \
00400                           _U8_to_str((obj >> 24) & 0xff)
00401 
00402             # if it is a code object
00403             elif objtype == types.CodeType:
00404                 #determine if it's native or regular
00405                 if (len(obj.co_consts) > 0 and
00406                     (type(obj.co_consts[0]) == types.StringType) and
00407                     (obj.co_consts[0][0:NATIVE_INDICATOR_LENGTH] ==
00408                     NATIVE_INDICATOR)):
00409                     imgstr += self.no_to_str(obj)
00410                 else:
00411                     imgstr += self.co_to_str(obj)
00412 
00413             # if it is a tuple
00414             elif objtype == types.TupleType:
00415                 imgstr += self._seq_to_str(obj)
00416 
00417             # if it is None
00418             elif objtype == types.NoneType:
00419                 # marker, none (0)
00420                 imgstr += _U8_to_str(OBJ_TYPE_NON)
00421 
00422             # if it is a float
00423             elif objtype == types.FloatType and PM_FEATURES["HAVE_FLOAT"]:
00424                 imgstr += _U8_to_str(OBJ_TYPE_FLT) + self._float_to_str(obj)
00425 
00426             # other type?
00427             else:
00428                 raise exceptions.NotImplementedError(
00429                           "Unhandled type %s." % objtype)
00430         return imgstr
00431 
00432 
00433     # NOTE: if the total size of the fixed-sized fields changes,
00434     # be sure to change CO_IMG_FIXEDPART_SIZE above
00435     def co_to_str(self, co):
00436         """Converts a Python code object to a PyMite image.
00437 
00438         The code image is relocatable and goes in the device's
00439         memory. Return string shows type in the leading byte.
00440         """
00441 
00442         # filter code object elements
00443         consts, names, code, nativecode = self._filter_co(co)
00444 
00445         # Type and size inserted below
00446 
00447         # Appends various constant-sized fields to the image
00448         imgstr = self._U8_to_str(co.co_argcount) + \
00449                  self._U8_to_str(co.co_flags & 0xFF) + \
00450                  self._U8_to_str(co.co_stacksize) + \
00451                  self._U8_to_str(co.co_nlocals)
00452 
00453         # Issue #56: Add support for closures
00454         if PM_FEATURES["HAVE_CLOSURES"]:
00455             imgstr += self._U8_to_str(len(co.co_freevars))
00456 
00457         # Issue #103: Add debug info to exception reports
00458         if PM_FEATURES["HAVE_DEBUG_INFO"]:
00459             imgstr += self._U16_to_str(co.co_firstlineno)
00460 
00461         # Variable length objects
00462         # Appends names (tuple) to the image
00463         s = self._seq_to_str(names)
00464         lennames = len(s)
00465         imgstr += s
00466 
00467         # Issue #103: Add debug info to exception reports
00468         lenlnotab = 0
00469         lenfilename = 0
00470         if PM_FEATURES["HAVE_DEBUG_INFO"]:
00471 
00472             # Appends line number table (string) to the image
00473             assert len(co.co_lnotab) <= MAX_STRING_LEN
00474             s = self._U8_to_str(OBJ_TYPE_STR) + \
00475                 self._U16_to_str(len(co.co_lnotab)) + co.co_lnotab
00476             lenlnotab = len(s)
00477             imgstr += s
00478 
00479             # Appends filename (string) to the image
00480             # fn = os.path.split(co.co_filename)[-1]
00481             fn = co.co_filename
00482             assert len(fn) <= MAX_STRING_LEN
00483             s = self._U8_to_str(OBJ_TYPE_STR) + \
00484                 self._U16_to_str(len(fn) + 1) + fn + '\0'
00485             lenfilename = len(s)
00486             imgstr += s
00487 
00488         # Appends consts tuple to the image
00489         s = self._seq_to_str(consts)
00490         lenconsts = len(s)
00491         imgstr += s
00492 
00493         # Issue #56: Add support for closures
00494         # Appends cellvars tuple to the image
00495         if PM_FEATURES["HAVE_CLOSURES"]:
00496             # Lookup the index of the cellvar name in co_varnames; -1 if not in
00497             l = [-1,] * len(co.co_cellvars)
00498             for i,name in enumerate(co.co_cellvars):
00499                 if name in co.co_varnames:
00500                     l[i] = list(co.co_varnames).index(name)
00501             s = self._seq_to_str(tuple(l))
00502             lenconsts += len(s)
00503             imgstr += s
00504 
00505         # Appends bytecode (or native index) to image
00506         imgstr += code
00507 
00508         size = CO_IMG_FIXEDPART_SIZE + lennames + lenlnotab + lenfilename + \
00509                lenconsts + len(code)
00510 
00511         # Inserts type and size (skipped earlier)
00512         imgstr = self._U8_to_str(OBJ_TYPE_CIM) + \
00513                  self._U16_to_str(size) + \
00514                  imgstr
00515         assert len(imgstr) <= MAX_IMG_LEN
00516 
00517         return imgstr
00518 
00519 
00520     def no_to_str(self, co):
00521         """Convert a native code object to a PyMite image.
00522 
00523         The native image is relocatable and goes in the device's
00524         memory. Return string shows type in the leading byte.
00525         """
00526 
00527         # filter code object elements
00528         consts, names, code, nativecode = self._filter_co(co)
00529 
00530         # list of strings to build image
00531 
00532         # set image type byte
00533         objtype = OBJ_TYPE_NIM
00534 
00535         # Create native image string
00536         # (type, argcount, funcindex)
00537         imgstr = (self._U8_to_str(OBJ_TYPE_NIM) +
00538                   self._U8_to_str(co.co_argcount) +
00539                   code)
00540 
00541         # ensure string length fits within S16 type
00542         assert len(imgstr) <= MAX_IMG_LEN
00543 
00544         return imgstr
00545 
00546 
00547 ################################################################
00548 # FILTER FUNCTION
00549 ################################################################
00550 
00551     def _filter_co(self, co):
00552         """Run the Python code obj, co, through various filters.
00553 
00554         Ensure it is compliant with PyMite restrictions.
00555 
00556         Consts filter:
00557             Ensure num consts is less than 256.
00558             Replace __doc__ with None if present.
00559 
00560         Flags filter:
00561             Check co_flags for flags that indicate an unsupported feature
00562             Supported flags: CO_NOFREE, CO_OPTIMIZED, CO_NEWLOCALS, CO_NESTED,
00563             Unsupported flags: CO_VARARGS, CO_VARKEYWORDS
00564             Conditionally supported flags: CO_GENERATOR if HAVE_GENERATORS
00565 
00566         Native code filter:
00567             If this function has a native indicator,
00568             extract the native code from the doc string
00569             and clear the doc string.
00570             Ensure num args is less or equal to
00571             NATIVE_MAX_NUM_LOCALS.
00572 
00573         Names/varnames filter:
00574             Ensure num names is less than 256.
00575             If co_name is the module identifier replace it with
00576             the trimmed module name
00577             otherwise just append the name to co_name.
00578 
00579         Bcode filter:
00580             Raise NotImplementedError for an invalid bcode.
00581 
00582         If all is well, return the filtered consts list,
00583         names list, code string and native code.
00584         """
00585 
00586         ## General filter
00587         # ensure values fit within U8 type size
00588         assert len(co.co_consts) < 256, "too many constants."
00589         assert len(co.co_names) < 256, "too many names."
00590         assert co.co_argcount < 256, "too many arguments."
00591         assert co.co_stacksize < 256, "too large of a stack."
00592         assert co.co_nlocals < 256, "too many local variables."
00593 
00594         # make consts a list so a single element can be modified
00595         consts = list(co.co_consts)
00596 
00597         # Check co_flags
00598         unsupported_flags = CO_VARARGS | CO_VARKEYWORDS
00599         if not PM_FEATURES["HAVE_GENERATORS"]:
00600             unsupported_flags |= CO_GENERATOR
00601         assert co.co_flags & unsupported_flags == 0,\
00602             "Unsupported code identified by co_flags (%s)." % hex(co.co_flags)
00603 
00604         # get trimmed src file name and module name
00605         fn = os.path.basename(co.co_filename)
00606         mn = os.path.splitext(fn)[0]
00607 
00608         # init native code
00609         nativecode = None
00610 
00611         ## Bcode filter
00612         # bcode string
00613         s = co.co_code
00614         # filtered code string
00615         code = ""
00616         # iterate through the string
00617         lno = 0
00618         i = 0
00619         len_s = len(s)
00620         while i < len_s:
00621 
00622             #get char
00623             c = ord(s[i])
00624 
00625             #ensure no illegal bytecodes are present
00626             if self.bcodes[c] == None:
00627                 raise NotImplementedError(
00628                         "Illegal bytecode (%d/%s/%s) "
00629                         "comes at offset %d in file %s." %
00630                         (c, hex(c), dis.opname[c],
00631                          i, co.co_filename))
00632 
00633             #if simple bcode, copy one byte
00634             if c < dis.HAVE_ARGUMENT:
00635                 code += s[i]
00636                 i += 1
00637 
00638             #else copy three bytes
00639             else:
00640 
00641                 # Raise error if default arguments exist and are not configured
00642                 if (not PM_FEATURES["HAVE_DEFAULTARGS"]
00643                     and c == dis.opmap["MAKE_FUNCTION"]
00644                     and self._str_to_U16(s[i+1:i+3]) > 0):
00645 
00646                     raise NotImplementedError(
00647                             "Bytecode (%d/%s/%s) not configured "
00648                             "to support default arguments; "
00649                             "comes at offset %d in file %s." %
00650                             (c, hex(c), dis.opname[c], i, co.co_filename))
00651 
00652                 # Otherwise, copy the code (3 bytes)
00653                 code += s[i:i+3]
00654                 i += 3
00655 
00656         # if the first const is a String,
00657         if (len(consts) > 0 and type(consts[0]) == types.StringType):
00658 
00659             ## Native code filter
00660             # if this CO is intended to be a native func.
00661             if (consts[0][:NATIVE_INDICATOR_LENGTH] ==
00662                 NATIVE_INDICATOR):
00663 
00664                 # ensure num args is less or equal
00665                 # to NATIVE_MAX_NUM_LOCALS
00666                 assert co.co_nlocals <= NATIVE_MAX_NUM_LOCALS
00667 
00668                 # extract native code and clear doc string
00669                 nativecode = consts[0][NATIVE_INDICATOR_LENGTH:]
00670                 consts[0] = None
00671 
00672                 # If this co is a module
00673                 # Old #28: Module root must keep its bytecode
00674                 if co.co_name == MODULE_IDENTIFIER:
00675                     self.nativemods.append((co.co_filename, nativecode))
00676 
00677                 # Else this co is a function;
00678                 # replace code with native table index
00679                 else:
00680                     # stdlib code gets a positive index
00681                     if self.imgtarget == "std":
00682                         code = self._U16_to_str(len(self.nativetable))
00683                     # usr code gets a negative index
00684                     else:
00685                         code = self._U16_to_str(-len(self.nativetable))
00686 
00687                     # native function name is
00688                     # "nat_<modname>_<pyfuncname>".
00689                     # append (nat func name, nat code) to table
00690                     self.nativetable.append(
00691                         ("%s%02d_%s_%s"
00692                          % (NATIVE_FUNC_PREFIX, self.nfcount, mn, co.co_name),
00693                          nativecode))
00694                     self.nfcount += 1
00695 
00696             ## Consts filter
00697             # if want to remove __doc__ string
00698             # WARNING: this heuristic is not always accurate
00699             elif (REMOVE_DOC_STR and len(co.co_names) > 0
00700                   and co.co_names[0] == "__doc__"):
00701                 consts[0] = None
00702 
00703         ## Names filter
00704         names = list(co.co_names)
00705 
00706         # Remove __doc__ name if requested
00707         if REMOVE_DOC_STR and len(names) > 0 and names[0] == "__doc__":
00708             names[0] = ''
00709 
00710         # if co_name is the module identifier change it to module name
00711         if co.co_name == MODULE_IDENTIFIER:
00712             names.append(mn)
00713         # else use unmodified co_name
00714         else:
00715             names.append(co.co_name)
00716 
00717 
00718         return consts, names, code, nativecode
00719 
00720 
00721 ################################################################
00722 # IMAGE WRITING FUNCTIONS
00723 ################################################################
00724 
00725     def write_image_file(self,):
00726         """Writes an image file
00727         """
00728         fmtfxn = self.formatFromExt[self.imgtype]
00729         f = open(self.outfn, 'wb')
00730         f.write(fmtfxn())
00731         f.close()
00732 
00733     def write_native_file(self,):
00734         """Writes native functions if filename was given
00735         """
00736         if not self.nativeFilename:
00737             return
00738         f = open(self.nativeFilename, 'wb')
00739         f.write(self.format_native_table())
00740         f.close()
00741 
00742 
00743     def format_img_as_bin(self,):
00744         """format_img_as_bin() --> string
00745 
00746         Write image bytes to raw binary string.
00747         The resulting string is suitable to write to a file.
00748         """
00749 
00750         # no reformatting necessary, join all object images
00751         return string.join(self.imgDict["imgs"], "")
00752 
00753 
00754     def format_img_as_c(self,):
00755         """format_img_as_c() --> string
00756 
00757         Format image bytes to a string that is a C byte array.
00758         The C byte array can be located in RAM
00759         or program memory.  The byte array is named lib_img.
00760         """
00761 
00762         # list of filenames
00763         fns = self.imgDict["fns"]
00764         imgs = self.imgDict["imgs"]
00765 
00766         # create intro
00767         fileBuff = []
00768         fileBuff.append("/**\n"
00769                         " * PyMite library image file.\n"
00770                         " *\n"
00771                         " * Automatically created from:\n"
00772                         " * \t%s\n"
00773                         " * by pmImgCreator.py on\n"
00774                         " * %s.\n"
00775                         " * \n"
00776                         " * Byte count: %d\n"
00777                         " * \n"
00778                         " * Selected memspace type: %s\n"
00779                         " * \n"
00780                         " * DO NOT EDIT THIS FILE.\n"
00781                         " * ANY CHANGES WILL BE LOST.\n"
00782                         " */\n\n"
00783                         % (string.join(fns, "\n *\t"),
00784                            time.ctime(time.time()),
00785                            len(string.join(imgs, "")),
00786                            self.memspace.upper()
00787                           )
00788                        )
00789         fileBuff.append("/* Place the image into %s */\n"
00790                         "#ifdef __cplusplus\n"
00791                         "extern\n"
00792                         "#endif\n"
00793                         "unsigned char const\n"
00794                         % self.memspace.upper()
00795                        )
00796 
00797         if self.memspace.lower() == "flash":
00798             fileBuff.append("#if defined(__AVR__)\n"
00799                             "__attribute__((progmem))\n"
00800                             "#endif\n"
00801                            )
00802 
00803         fileBuff.append("%slib_img[] =\n"
00804                         "{\n"
00805                         % (self.imgtarget)
00806                        )
00807 
00808         # for each src file, convert and format
00809         i = 0
00810         for fn in fns:
00811 
00812             # get img string for this file
00813             img = imgs[i]
00814             i += 1
00815 
00816             # print all bytes
00817             fileBuff.append("\n\n/* %s */" % fn)
00818             j = 0
00819             while j < len(img):
00820                 if (j % 8) == 0:
00821                     fileBuff.append("\n    ")
00822                 fileBuff.append("0x%02X, " % ord(img[j]))
00823                 j += 1
00824 
00825         # finish off array
00826         fileBuff.append("\n};\n")
00827 
00828         return string.join(fileBuff, "")
00829 
00830 
00831     def format_native_table(self,):
00832         """format_native_table() --> string
00833 
00834         Format native table to a C file containing
00835         native functions and a function table.
00836         """
00837         # create intro
00838         fileBuff = []
00839         fileBuff.append("#undef __FILE_ID__\n"
00840                         "#define __FILE_ID__ 0x0A\n"
00841                         "/**\n"
00842                         " * PyMite %s native function file\n"
00843                         " *\n"
00844                         " * automatically created by pmImgCreator.py\n"
00845                         " * on %s\n"
00846                         " *\n"
00847                         " * DO NOT EDIT THIS FILE.\n"
00848                         " * ANY CHANGES WILL BE LOST.\n"
00849                         " *\n"
00850                         " * @file    %s\n"
00851                         " */\n\n"
00852                         "#define __IN_LIBNATIVE_C__\n"
00853                         "#include \"pm.h\"\n\n"
00854                         % (self.imgtarget,
00855                            time.ctime(time.time()),
00856                            self.nativeFilename
00857                           )
00858                        )
00859 
00860         # module-level native sections (for #include headers)
00861         for (modname, modstr) in self.nativemods:
00862             fileBuff.append("/* From: %s */%s\n" % (modname, modstr))
00863 
00864         # for each entry create fxn
00865         for (funcname, funcstr) in self.nativetable:
00866             fileBuff.append("PmReturn_t\n"
00867                             "%s(pPmFrame_t *ppframe)\n"
00868                             "{\n"
00869                             "%s\n"
00870                             "}\n\n" % (funcname, funcstr))
00871 
00872         # create fxn table
00873         fileBuff.append("/* Native function lookup table */\n"
00874                         "pPmNativeFxn_t const %s[] =\n"
00875                         "{\n" % NATIVE_TABLE_NAME[self.imgtarget])
00876 
00877         # put all native funcs in the table
00878         for (funcname, funcstr) in self.nativetable:
00879             fileBuff.append("    %s,\n" % funcname)
00880         fileBuff.append("};\n")
00881 
00882         return string.join(fileBuff, "")
00883 
00884 
00885 ################################################################
00886 # MAIN
00887 ################################################################
00888 
00889 def parse_cmdline():
00890     """Parses the command line for options.
00891     """
00892     try:
00893         opts, args = getopt.getopt(sys.argv[1:],
00894                                    "f:bcsuo:",
00895                                    ["memspace=", "native-file="])
00896     except:
00897         print __usage__
00898         sys.exit(EX_USAGE)
00899 
00900     # Parse opts for the image type to write
00901     imgtype = ".c"
00902     imgtarget = "std"
00903     memspace = "ram"
00904     outfn = None
00905     nativeFilename = None
00906     for opt in opts:
00907         if opt[0] == "-b":
00908             imgtype = ".bin"
00909         elif opt[0] == "-c":
00910             imgtype = ".c"
00911         elif opt[0] == "-s":
00912             imgtarget = "std"
00913         elif opt[0] == "-u":
00914             imgtarget = "usr"
00915         elif opt[0] == "--memspace":
00916             # Error if memspace switch given without arg
00917             if not opt[1] or (opt[1].lower() not in ["ram", "flash"]):
00918                 print "Only one of these memspace types allowed: ram, flash"
00919                 print __usage__
00920                 sys.exit(EX_USAGE)
00921             memspace = opt[1]
00922         elif opt[0] == "--native-file":
00923             # Error if switch given without arg
00924             if not opt[1]:
00925                 print "Specify a filename like this: --native-file=libnative.c"
00926                 print __usage__
00927                 sys.exit(EX_USAGE)
00928             nativeFilename = opt[1]
00929         elif opt[0] == "-f":
00930             pmfeatures_filename = opt[1]
00931         elif opt[0] == "-o":
00932             # Error if out filename switch given without arg
00933             if not opt[1]:
00934                 print __usage__
00935                 sys.exit(EX_USAGE)
00936             outfn = opt[1]
00937 
00938     # Error if no image type was given
00939     if not imgtype:
00940         print __usage__
00941         sys.exit(EX_USAGE)
00942 
00943     # Error if no input filenames are given
00944     if len(args) == 0:
00945         print __usage__
00946         sys.exit(EX_USAGE)
00947 
00948     return outfn, imgtype, imgtarget, memspace, nativeFilename, args, pmfeatures_filename
00949 
00950 
00951 def main():
00952     outfn, imgtyp, imgtarget, memspace, natfn, fns, pmfn = parse_cmdline()
00953     pic = PmImgCreator(pmfn)
00954     pic.set_options(outfn, imgtyp, imgtarget, memspace, natfn, fns)
00955     pic.convert_files()
00956     pic.write_image_file()
00957     pic.write_native_file()
00958 
00959 
00960 if __name__ == "__main__":
00961     main()

Generated on Mon Oct 18 07:40:47 2010 for Python-on-a-chip by  doxygen 1.5.9