PKJ[AAwasmfun/README.mdThis is the Python lib providing functionality to generate WASM. PKTJ5bϧwasmfun/__init__.py""" A Python library that provides tools to handle WASM code. """ __version__ = '0.1' from ._opcodes import OPCODES, I from .components import * from .util import * PKxJ9Awasmfun/_opcodes.py""" A dict with WASM opcodes. """ # Note: left out 32bit opcodes at first. Added them later, but I might have missed some. # todo: would be nice to also know how many args the opcode needs, so that we can validate that OPCODES = { 'unreachable':0x00, 'nop':0x01, 'block':0x02, 'loop':0x03, 'if':0x04, 'else':0x05, 'end':0x0b, 'br':0x0c, 'br_if':0x0d, 'br_table':0x0e, 'return':0x0f, 'call':0x10, 'call_indirect':0x11, 'drop':0x1a, 'select':0x1b, 'get_local':0x20, 'set_local':0x21, 'tee_local':0x22, 'get_global':0x23, 'set_global':0x24, 'i32.load':0x28, 'i64.load':0x29, 'f32.load':0x2a, 'f64.load':0x2b, 'i32.load8_s':0x2c, 'i32.load8_u':0x2d, 'i32.load16_s':0x2e, 'i32.load16_u':0x2f, 'i64.load8_s':0x30, 'i64.load8_u':0x31, 'i64.load16_s':0x32, 'i64.load16_u':0x33, 'i64.load32_s':0x34, 'i64.load32_u':0x35, 'i32.store8':0x3a, 'i32.store16':0x3b, 'i32.store':0x36, 'i64.store':0x37, 'f32.store':0x38, 'f64.store':0x39, 'current_memory':0x3f, 'grow_memory':0x40, 'i32.const':0x41, 'i64.const':0x42, 'f32.const':0x43, 'f64.const':0x44, 'i32.eqz':0x45, 'i32.eq':0x46, 'i32.ne':0x47, 'i32.lt_s':0x48, 'i32.lt_u':0x49, 'i32.gt_s':0x4a, 'i32.gt_u':0x4b, 'i32.le_s':0x4c, 'i32.le_u':0x4d, 'i32.ge_s':0x4e, 'i32.ge_u':0x4f, 'i64.eqz':0x50, 'i64.eq':0x51, 'i64.ne':0x52, 'i64.lt_s':0x53, 'i64.lt_u':0x54, 'i64.gt_s':0x55, 'i64.gt_u':0x56, 'i64.le_s':0x57, 'i64.le_u':0x58, 'i64.ge_s':0x59, 'i64.ge_u':0x5a, 'f32.eq':0x5b, 'f32.ne':0x5c, 'f32.lt':0x5d, 'f32.gt':0x5e, 'f32.le':0x5f, 'f32.ge':0x60, 'f64.eq':0x61, 'f64.ne':0x62, 'f64.lt':0x63, 'f64.gt':0x64, 'f64.le':0x65, 'f64.ge':0x66, 'i32.clz':0x67, 'i32.ctz':0x68, 'i32.popcnt':0x69, 'i32.add':0x6a, 'i32.sub':0x6b, 'i32.mul':0x6c, 'i32.div_s':0x6d, 'i32.div_u':0x6e, 'i32.rem_s':0x6f, 'i32.rem_u':0x70, 'i32.and':0x71, 'i32.or':0x72, 'i32.xor':0x73, 'i32.shl':0x74, 'i32.shr_s':0x75, 'i32.shr_u':0x76, 'i32.rotl':0x77, 'i32.rotr':0x78, 'i64.clz':0x79, 'i64.ctz':0x7a, 'i64.popcnt':0x7b, 'i64.add':0x7c, 'i64.sub':0x7d, 'i64.mul':0x7e, 'i64.div_s':0x7f, 'i64.div_u':0x80, 'i64.rem_s':0x81, 'i64.rem_u':0x82, 'i64.and':0x83, 'i64.or':0x84, 'i64.xor':0x85, 'i64.shl':0x86, 'i64.shr_s':0x87, 'i64.shr_u':0x88, 'i64.rotl':0x89, 'i64.rotr':0x8a, 'f32.abs':0x8b, 'f32.neg':0x8c, 'f32.ceil':0x8d, 'f32.floor':0x8e, 'f32.trunc':0x8f, 'f32.nearest':0x90, 'f32.sqrt':0x91, 'f32.add':0x92, 'f32.sub':0x93, 'f32.mul':0x94, 'f32.div':0x95, 'f32.min':0x96, 'f32.max':0x97, 'f32.copysign':0x98, 'f64.abs':0x99, 'f64.neg':0x9a, 'f64.ceil':0x9b, 'f64.floor':0x9c, 'f64.trunc':0x9d, 'f64.nearest':0x9e, 'f64.sqrt':0x9f, 'f64.add':0xa0, 'f64.sub':0xa1, 'f64.mul':0xa2, 'f64.div':0xa3, 'f64.min':0xa4, 'f64.max':0xa5, 'f64.copysign':0xa6, 'i32.wrap_i64':0xa7, 'i32.trunc_s_f32':0xa8, 'i32.trunc_u_f32':0xa9, 'i32.trunc_s_f64':0xaa, 'i32.trunc_u_f64':0xab, 'i64.extend_s_i32':0xac, 'i64.extend_u_i32':0xad, 'i64.trunc_s_f32':0xae, 'i64.trunc_u_f32':0xaf, 'i64.trunc_s_f64':0xb0, 'i64.trunc_u_f64':0xb1, 'f32.convert_s_i32':0xb2, 'f32.convert_u_i32':0xb3, 'f32.convert_s_i64':0xb4, 'f32.convert_u_i64':0xb5, 'f32.demote_f64':0xb6, 'f64.convert_s_i32':0xb7, 'f64.convert_u_i32':0xb8, 'f64.convert_s_i64':0xb9, 'f64.convert_u_i64':0xba, 'f64.promote_f32':0xbb, 'i32.reinterpret_f32':0xbc, 'i64.reinterpret_f64':0xbd, 'f32.reinterpret_i32':0xbe, 'f64.reinterpret_i64':0xbf, } # Generate an instructionset object that supports autocompletion class Instructionset: pass def _make_instructionset(): main_i = Instructionset() for opcode in OPCODES: parts = opcode.split('.') i = main_i for part in parts[:-1]: if not hasattr(i, part): setattr(i, part, Instructionset()) i = getattr(i, part) setattr(i, parts[-1], opcode) return main_i I = _make_instructionset() PKi`J>^^wasmfun/components.py""" The field classes to represent a WASM program. """ from io import BytesIO from struct import pack as spack from . import OPCODES LANG_TYPES = { 'i32': b'\x7f', 'i64': b'\x7e', 'f32': b'\x7d', 'f64': b'\x7c', 'anyfunc': b'\x70', 'func': b'\x60', 'emptyblock': b'\x40', # pseudo type for representing an empty block_type } def packf64(x): return spack('>= 7 unsignedRefValue >>= 7 if unsignedRefValue != 0: byte = byte | 0x80 bb.append(byte) if unsignedRefValue == 0: break return bytes(bb) def unsigned_leb128_encode(value): bb = [] # ints, really while True: byte = value & 0x7F value >>= 7 if value != 0: byte = byte | 0x80 bb.append(byte) if value == 0: break return bytes(bb) class WASMComponent: """Base class for representing components of a WASM module, from the module to sections and instructions. These components can be shown as text or written as bytes. Methods: * `to_bytes()` - Get the bytes that represent the binary WASM for this component. * `show()` - Print a textual representation of the component. * `to_file(f)` - Write the binary representation of this component to a file. * `to_text()` - Return a textual representation of this component. """ __slots__ = [] def __init__(self): pass def __repr__(self): return '' % self.__class__.__name__ def _get_sub_text(self, subs, multiline=False): # Collect sub texts texts = [] charcount = 0 haslf = False for sub in subs: if isinstance(sub, WASMComponent): text = sub.to_text() else: text = repr(sub) charcount += len(text) texts.append(text) haslf = haslf or '\n' in text # Put on one line or multiple lines if multiline or haslf or charcount > 70: lines = [] for text in texts: for line in text.splitlines(): lines.append(line) lines = [' ' + line for line in lines] return '\n'.join(lines) else: return ', '.join(texts) def to_bytes(self): """ Get the bytes that represent the binary WASM for this component. """ f = BytesIO() self.to_file(f) return f.getvalue() def show(self): """ Print a textual representation of the component. """ print(self.to_text()) def to_file(self, f): """ Write the binary representation of this component to a file. Implemented in the subclasses. """ raise NotImplementedError() def to_text(self): """ Return a textual representation of this component. Implemented in the subclasses. """ raise NotImplementedError() class Module(WASMComponent): """ Class to represent a WASM module; the toplevel unit of code. The subcomponents of a module are objects that derive from `Section`. It is recommended to provide `Function` and `ImportedFunction` objects, from which the module will polulate the function-related sections, and handle the binding of the function index space. """ __slots__ = ['sections', 'func_id_to_index'] def __init__(self, *sections): # Process sections, filter out high-level functions self.sections = [] has_lowlevel_funcs = False functions = [] start_section = None import_section = None export_section = None for section in sections: if isinstance(section, (Function, ImportedFuncion)): functions.append(section) elif isinstance(section, Section): self.sections.append(section) if isinstance(section, (TypeSection, CodeSection)): has_lowlevel_funcs = True elif isinstance(section, StartSection): start_section = section elif isinstance(section, ImportSection): import_section = section elif isinstance(section, ExportSection): export_section = section else: raise TypeError('Module expects a Function, ImportedFunction, or a Section.') # Process high level function desctiptions? if functions and has_lowlevel_funcs: raise TypeError('Module cannot have both Functions/ImportedFunctions and FunctionDefs/FunctionSigs.') elif functions: self._process_functions(functions, start_section, import_section, export_section) # Sort the sections self.sections.sort(key=lambda x: x.id) # Prepare functiondefs for section in reversed(self.sections): if isinstance(section, CodeSection): for funcdef in section.functiondefs: funcdef.module = self break def _process_functions(self, functions, start_section, import_section, export_section): # Prepare processing functions. In the order of imported and then defined, # because that's the order of the function index space. We use the function # index also for the signature index, though that's not strictly necessary # (e.g. functions can share a signature). # Function index space is used in, calls, exports, elementes, start function. auto_sigs = [] auto_defs = [] auto_imports = [] auto_exports = [] auto_start = None function_index = 0 self.func_id_to_index = {} # Process imported functions for func in functions: if isinstance(func, ImportedFuncion): auto_sigs.append(FunctionSig(func.params, func.returns)) auto_imports.append(Import(func.modname, func.fieldname, 'function', function_index)) if func.export: auto_exports.append((func.idname, 'function', function_index)) self.func_id_to_index[func.idname] = function_index function_index += 1 # Process defined functions for func in functions: if isinstance(func, Function): auto_sigs.append(FunctionSig(func.params, func.returns)) auto_defs.append(FunctionDef(func.locals, *func.instructions)) if func.export: auto_exports.append((func.idname, 'function', function_index)) if func.idname == '$main' and start_section is None: auto_start = StartSection(function_index) self.func_id_to_index[func.idname] = function_index function_index += 1 # Insert auto-generated function sigs and defs self.sections.append(TypeSection(*auto_sigs)) self.sections.append(CodeSection(*auto_defs)) # Insert auto-generated imports if import_section is None: import_section = ImportSection() self.sections.append(import_section) import_section.imports.extend(auto_imports) # Insert auto-generated exports if export_section is None: export_section = ExportSection() self.sections.append(export_section) export_section.exports.extend(auto_exports) # Insert function section self.sections.append(FunctionSection(*range(len(auto_imports), function_index))) # Insert start section if auto_start is not None: self.sections.append(auto_start) def to_text(self): return 'Module(\n' + self._get_sub_text(self.sections, True) + '\n)' def to_file(self, f): f.write(b'\x00asm') f.write(packu32(1)) # version, must be 1 for now for section in self.sections: section.to_file(f) class Function: """ High-level description of a function. The linking is resolved by the module. """ __slots__ = ['idname', 'params', 'returns', 'locals', 'instructions', 'export'] def __init__(self, idname, params=None, returns=None, locals=None, instructions=None, export=False): assert isinstance(idname, str) assert isinstance(params, (tuple, list)) assert isinstance(returns, (tuple, list)) assert isinstance(locals, (tuple, list)) assert isinstance(instructions, (tuple, list)) self.idname = idname self.params = params self.returns = returns self.locals = locals self.instructions = instructions self.export = bool(export) class ImportedFuncion: """ High-level description of an imported function. The linking is resolved by the module. """ __slots__ = ['idname', 'params', 'returns', 'modname', 'fieldname', 'export'] def __init__(self, idname, params, returns, modname, fieldname, export=False): assert isinstance(idname, str) assert isinstance(params, (tuple, list)) assert isinstance(returns, (tuple, list)) assert isinstance(modname, str) assert isinstance(fieldname, str) self.idname = idname self.params = params self.returns = returns self.modname = modname self.fieldname = fieldname self.export = bool(export) ## Sections class Section(WASMComponent): """Base class for module sections. """ __slots__ = [] id = -1 def to_text(self): return '%s()' % self.__class__.__name__ def to_file(self, f): f2 = BytesIO() self.get_binary_section(f2) payload = f2.getvalue() id = self.id assert id >= 0 # Write it all f.write(packvu7(id)) f.write(packvu32(len(payload))) if id == 0: # custom section for debugging, future, or extension type = self.__cass__.__name__.lower().split('section')[0] f.write(pack_str(type)) f.write(payload) def get_binary_section(self, f): raise NotImplementedError() # Sections need to implement this class TypeSection(Section): """ Defines signatures of functions that are either imported or defined in this module. """ __slots__ = ['functionsigs'] id = 1 def __init__(self, *functionsigs): for i, functionsig in enumerate(functionsigs): assert isinstance(functionsig, FunctionSig) # todo: remove this? functionsig.index = i # so we can resolve the index in Import objects self.functionsigs = functionsigs def to_text(self): return 'TypeSection(\n' + self._get_sub_text(self.functionsigs, True) + '\n)' def get_binary_section(self, f): f.write(packvu32(len(self.functionsigs))) # count for functionsig in self.functionsigs: functionsig.to_file(f) class ImportSection(Section): """ Defines the things to be imported in a module. """ __slots__ = ['imports'] id = 2 def __init__(self, *imports): for imp in imports: assert isinstance(imp, Import) self.imports = list(imports) def to_text(self): return 'ImportSection(\n' + self._get_sub_text(self.imports, True) + '\n)' def get_binary_section(self, f): f.write(packvu32(len(self.imports))) # count for imp in self.imports: imp.to_file(f) class FunctionSection(Section): """ Declares for each function defined in this module which signature is associated with it. The items in this sections match 1-on-1 with the items in the code section. """ __slots__ = ['indices'] id = 3 def __init__(self, *indices): for i in indices: assert isinstance(i, int) self.indices = indices # indices in the Type section def to_text(self): return 'FunctionSection(' + ', '.join([str(i) for i in self.indices]) + ')' def get_binary_section(self, f): f.write(packvu32(len(self.indices))) for i in self.indices: f.write(packvu32(i)) class TableSection(Section): """ Define stuff provided by the host system that WASM can use/reference, but cannot be expressed in WASM itself. """ __slots__ = [] id = 4 class MemorySection(Section): """ Declares initial (and max) sizes of linear memory, expressed in WASM pages (64KiB). Only one default memory can exist in the MVP. """ __slots__ = ['entries'] id = 5 def __init__(self, *entries): assert len(entries) == 1 # in MVP self.entries = entries def to_text(self): return 'MemorySection(' + ', '.join([str(i) for i in self.entries]) + ')' def get_binary_section(self, f): f.write(packvu32(len(self.entries))) for entrie in self.entries: # resizable_limits if isinstance(entrie, int): entrie = (entrie, ) if len(entrie) == 1: f.write(packvu1(0)) f.write(packvu32(entrie[0])) # initial, no max else: f.write(packvu1(1)) f.write(packvu32(entrie[0])) # initial f.write(packvu32(entrie[1])) # maximum class GlobalSection(Section): """ Defines the globals in a module. WIP. """ __slots__ = [] id = 6 class ExportSection(Section): """ Defines the names that this module exports. """ __slots__ = ['exports'] id = 7 def __init__(self, *exports): for export in exports: assert isinstance(export, Export) self.exports = list(exports) def to_text(self): return 'ExportSection(\n' + self._get_sub_text(self.exports, True) + '\n)' def get_binary_section(self, f): f.write(packvu32(len(self.exports))) for export in self.exports: export.to_file(f) class StartSection(Section): """ Provide the index of the function to call at init-time. The func must have zero params and return values. """ __slots__ = ['index'] id = 8 def __init__(self, index): self.index = index def to_text(self): return 'StartSection(' + str(self.index) + ')' def get_binary_section(self, f): f.write(packvu32(self.index)) class ElementSection(Section): """ To initialize table elements. WIP. """ __slots__ = [] id = 9 class CodeSection(Section): """ The actual code for a module, one CodeSection per function. """ __slots__ = ['functiondefs'] id = 10 def __init__(self, *functiondefs): for functiondef in functiondefs: assert isinstance(functiondef, FunctionDef) self.functiondefs = functiondefs def to_text(self): return 'CodeSection(\n' + self._get_sub_text(self.functiondefs, True) + '\n)' def get_binary_section(self, f): f.write(packvu32(len(self.functiondefs))) for functiondef in self.functiondefs: functiondef.to_file(f) class DataSection(Section): """ Initialize the linear memory. Note that the initial contents of linear memory are zero. """ __slots__ = ['chunks'] id = 11 def __init__(self, *chunks): self.chunks = [] for chunk in chunks: assert len(chunk) == 3 # index, offset, bytes assert chunk[0] == 0 # always 0 in MVP assert isinstance(chunk[2], bytes) def to_text(self): chunkinfo = [(chunk[0], chunk[1], len(chunk[2])) for chunk in self.chunks] return 'DataSection(' + ', '.join([str(i) for i in chunkinfo]) + ')' def get_binary_section(self, f): f.write(packvu32(len(self.chunks))) for chunk in self.chunks: f.write(packvu32(chunk[0])) #ff.write(packvu32(chunk[1])) Instruction('i32.const', chunk[1]).to_file(f) # todo: is this right? f.write(packvu32(len(chunk[2]))) ## Non-section components class Import(WASMComponent): """ Import objects (from other wasm modules or from the host environment). The type argument is an index in the type-section (signature) for funcs and a string type for table, memory and global. """ __slots__ = ['modname', 'fieldname', 'kind', 'type'] def __init__(self, modname, fieldname, kind, type): self.modname = modname self.fieldname = fieldname self.kind = kind self.type = type # the signature-index for funcs, the type for table, memory or global def to_text(self): return 'Import(%r, %r, %r, %r)' % (self.modname, self.fieldname, self.kind, self.type) def to_file(self, f): f.write(packstr(self.modname)) f.write(packstr(self.fieldname)) if self.kind == 'function': f.write(b'\x00') f.write(packvu32(self.type)) else: raise RuntimeError('Can only import functions for now') class Export(WASMComponent): """ Export an object defined in this module. The index is the index in the corresponding index space (e.g. for functions this is the function index space which is basically the concatenation of functions in the import and code sections). """ __slots__ = ['name', 'kind', 'index'] def __init__(self, name, kind, index): self.name = name self.kind = kind self.index = index def to_text(self): return 'Export(%r, %r, %i)' % (self.name, self.kind, self.index) def to_file(self, f): f.write(packstr(self.name)) if self.kind == 'function': f.write(b'\x00') f.write(packvu32(self.index)) else: raise RuntimeError('Can only export functions for now') class FunctionSig(WASMComponent): """ Defines the signature of a WASM module that is imported or defined in this module. """ __slots__ = ['params', 'returns', 'index'] def __init__(self, params=(), returns=()): self.params = params self.returns = returns self.index = None def to_text(self): return 'FunctionSig(%r, %r)' % (list(self.params), list(self.returns)) def to_file(self, f): f.write(b'\x60') # form -> nonfunctions may also be supported in the future f.write(packvu32(len(self.params))) # params for paramtype in self.params: f.write(LANG_TYPES[paramtype]) f.write(packvu1(len(self.returns))) # returns for rettype in self.returns: f.write(LANG_TYPES[rettype]) class FunctionDef(WASMComponent): """ The definition (of the body) of a function. The instructions can be Instruction instances or strings/tuples describing the instruction. """ __slots__ = ['locals', 'instructions', 'module'] def __init__(self, locals, *instructions): for loc in locals: assert isinstance(loc, str) # valuetype self.locals = locals self.instructions = [] for instruction in instructions: if isinstance(instruction, str): instruction = Instruction(instruction) elif isinstance(instruction, tuple): instruction = Instruction(*instruction) assert isinstance(instruction, Instruction) self.instructions.append(instruction) self.module = None def to_text(self): s = 'FunctionDef(' + str(list(self.locals)) + '\n' s += self._get_sub_text(self.instructions, True) s += '\n)' return s def to_file(self, f): # Collect locals by type local_entries = [] # list of (count, type) tuples for loc_type in self.locals: if local_entries and local_entries[-1] == loc_type: local_entries[-1] = local_entries[-1][0] + 1, loc_type else: local_entries.append((1, loc_type)) f3 = BytesIO() f3.write(packvu32(len(local_entries))) # number of local-entries in this func for localentry in local_entries: f3.write(packvu32(localentry[0])) # number of locals of this type f3.write(LANG_TYPES[localentry[1]]) for instruction in self.instructions: instruction.to_file(f3, self.module) f3.write(b'\x0b') # end body = f3.getvalue() f.write(packvu32(len(body))) # number of bytes in body f.write(body) class Instruction(WASMComponent): """ Class ro represent an instruction. Can have nested instructions, which really just come after it (so it only allows semantic sugar for blocks and loops. """ __slots__ = ['type', 'instructions', 'args'] def __init__(self, type, *args): self.type = type.lower() self.args = [] self.instructions = [] for arg in args: if isinstance(arg, WASMComponent): assert isinstance(arg, Instruction) self.instructions.append(arg) else: self.args.append(arg) def __repr__(self): return '' % self.type def to_text(self): subtext = self._get_sub_text(self.args) if '\n' in subtext: return 'Instruction(' + repr(self.type) + ',\n' + subtext + '\n)' else: return 'Instruction(' + repr(self.type) + ', ' + subtext + ')' def to_file(self, f, m): if self.type not in OPCODES: raise TypeError('Unknown instruction %r' % self.type) # Our instruction f.write(bytes([OPCODES[self.type]])) # Prep args args = self.args if self.type == 'call': if isinstance(args[0], str): args = [m.func_id_to_index[args[0]]] # Data comes after for arg in args: if isinstance(arg, (float, int)): if self.type.startswith('f64.'): f.write(packf64(arg)) elif self.type.startswith('i64.'): f.write(packvs64(arg)) elif self.type.startswith('i32.'): f.write(packvs32(arg)) elif self.type.startswith('i') or self.type.startswith('f'): raise RuntimeError('Unsupported instruction arg for %s' % self.type) else: f.write(packvu32(arg)) elif isinstance(arg, str): f.write(LANG_TYPES[arg]) else: raise TypeError('Unknown instruction arg %r' % arg) # todo: e.g. constants # Nested instructions for instruction in self.instructions: instruction.to_file(f, m) # Collect field classes _exportable_classes = WASMComponent, Function, ImportedFuncion __all__ = [name for name in globals() if isinstance(globals()[name], type) and issubclass(globals()[name], _exportable_classes)] PK-VJV~  wasmfun/generate_docs.py""" Script to generate simple docs for the wasmfun lib. """ import os import inspect import wasmfun as wf ROOT_DIR = os.path.dirname(os.path.dirname(__file__)) def make_sig(fun, name): args, varargs, varkw, defaults = inspect.getargspec(fun) # prepare defaults if defaults is None: defaults = () defaults = list(reversed(defaults)) # make list (back to forth) args2 = [] ismethod = args[0] == 'self' for i in range(len(args) - ismethod): arg = args.pop() if i < len(defaults): args2.insert(0, "%s=%s" % (arg, defaults[i])) else: args2.insert(0, arg) # append varargs and kwargs if varargs: args2.append("*" + varargs) if varkw: args2.append("**" + varkw) return "%s(%s)" % (name, ", ".join(args2) ) def get_docstring(fun): # assumes 4-space indentation for now. lines = [] doc = ' ' + fun.__doc__.strip() for line in doc.splitlines(): lines.append(line[4:]) lines.append('') return '\n'.join(lines) lines = ['# Documentation of wasmfun ' + wf.__version__, ''] ## Utilities lines += ['## Utility functions', ''] for name in wf.util.__all__: fun = getattr(wf.util, name) lines.append('#### function `%s`' % make_sig(fun, fun.__name__)) lines.append(get_docstring(fun)) lines.append('') lines.append('') ## Module building classes lines += ['## Module building classes', ''] lines.append(""" It is recommended to use the `Module`, `Function` and `ImportedFunction` classes, and add other sections as needed (`TypeSection`, `CodeSection` and `FunctionSection` should not be used in this case, since they are auto-generated). """) for name in wf.components.__all__: cls = getattr(wf.components, name) lines.append('#### class `%s`' % make_sig(cls.__init__, cls.__name__)) lines.append(get_docstring(cls)) lines.append('') lines.append('') ## OPCODES lines += ['## WASM opcodes', ''] lines += ['See `wasmfun.I` for an autocompletable structure representing all opcodes.', ''] for key in wf.OPCODES: lines.append('* `%s`' % key) lines.append('') ## Generate if __name__ == '__main__': with open(os.path.join(ROOT_DIR, 'DOCS.md'), 'wb') as f: text = '\n'.join(lines) f.write(text.encode()) PKeJGkkwasmfun/template.html

Code

CODE_PLACEHOLDER

Output

PKeJ--wasmfun/template.js/* This code is used to run WASM modules in either Nodejs or the browser. In both cases, a couple of standard functions are provided, e.g. to print text. */ WASM_PLACEHOLDER var is_node = typeof window === 'undefined'; /* Define functions to provide to the WASM module. */ function print_ln(x) { if (is_node) { process.stdout.write(x + '\n'); } else { var el = document.getElementById('wasm_output'); el.innerHTML += String(x).replace('\n', '
') + '
'; console.log(x); } } function print_charcode(i) { var c = String.fromCharCode(i); if (is_node) { process.stdout.write(c); } else { if (c == '\n') { c = '
'; } var el = document.getElementById('wasm_output'); el.innerHTML += c; } } function alert(x) { if (is_node) { process.stdout.write('ALERT: ' + x); } else { window.alert(x); } } function perf_counter() { if (is_node) { var t = process.hrtime(); return t[0] + t[1] * 1e-9; } else { return performance.now() * 1e-3; } } /* Pack importable funcs into a dict */ var providedfuncs = { print_ln: print_ln, print_charcode: print_charcode, alert: alert, perf_counter: perf_counter, }; function compile_my_wasm() { print_ln('Compiling wasm module'); var module_ = new WebAssembly.Module(wasm_data); print_ln('Initializing wasm module'); print_ln('Result:'); var module = new WebAssembly.Instance(module_, {js: providedfuncs}); print_ln('\n'); // flush } PKEfJwasmfun/util.py""" Utils for working with WASM and binary data. """ import os import tempfile import subprocess from .components import Module __all__ = ['inspect_bytes_at', 'hexdump', 'export_wasm_example', 'run_wasm_in_node', 'run_wasm_in_notebook'] def inspect_bytes_at(bb, offset): """ Inspect bytes at the specified offset. """ start = max(0, offset - 16) end = offset + 16 bytes2show = bb[start:end] bytes2skip = bb[start:offset] text_offset = len(repr(bytes2skip)) print(bytes2show) print('|'.rjust(text_offset)) def hexdump(bb): """ Do a hexdump of the given bytes. """ i = 0 line = 0 while i < len(bb): ints = [hex(j)[2:].rjust(2, '0') for j in bb[i:i+16]] print(str(line).rjust(8, '0'), *ints, sep=' ') i += 16 line += 1 def export_wasm_example(filename, code, wasm): """ Generate an html file for the given code and wasm module. """ if isinstance(wasm, Module): wasm = wasm.to_bytes() elif isinstance(wasm, bytes): if not wasm.startswith(b'\x00asm'): raise ValueError('export_wasm_example() given bytes do not look like a wasm module.') else: raise TypeError('export_wasm_example() expects a wasm module or bytes.') wasm_text = str(list(wasm)) # [0, 1, 12, ...] fname = os.path.basename(filename).rsplit('.', 1)[0] # Read templates src_filename_js = os.path.join(os.path.dirname(__file__), 'template.js') src_filename_html = os.path.join(os.path.dirname(__file__), 'template.html') with open(src_filename_js, 'rb') as f: js = f.read().decode() with open(src_filename_html, 'rb') as f: html = f.read().decode() # Produce HTML js = js.replace('WASM_PLACEHOLDER', 'var wasm_data = new Uint8Array(' + wasm_text + ');') html = html.replace('', '%s' % fname) html = html.replace('CODE_PLACEHOLDER', code) html = html.replace('JS_PLACEHOLDER', js) # Export HTML file with open(filename, 'wb') as f: f.write(html.encode()) print('Wrote example HTML to', filename) _nb_output = 0 def run_wasm_in_notebook(wasm): """ Load a WASM module in the Jupyter notebook. """ from IPython.display import display, HTML, Javascript if isinstance(wasm, Module): wasm = wasm.to_bytes() elif isinstance(wasm, bytes): if not wasm.startswith(b'\x00asm'): raise ValueError('run_wasm_in_notebook() given bytes do not look like a wasm module.') else: raise TypeError('run_wasm_in_notebook() expects a wasm module or bytes.') wasm_text = str(list(wasm)) # [0, 1, 12, ...] # Read templates src_filename_js = os.path.join(os.path.dirname(__file__), 'template.js') with open(src_filename_js, 'rb') as f: js = f.read().decode() # Get id global _nb_output _nb_output += 1 id = 'wasm_output_%u' % _nb_output # Produce JS js = js.replace('wasm_output', id) js = js.replace('WASM_PLACEHOLDER', 'var wasm_data = new Uint8Array(' + wasm_text + ');') js = '(function() {\n%s;\ncompile_my_wasm();\n})();' % js # Output in current cell display(HTML("
" % id)) display(Javascript(js)) def run_wasm_in_node(wasm): """ Load a WASM module in node. Just make sure that your module has a main function. """ if isinstance(wasm, Module): wasm = wasm.to_bytes() elif isinstance(wasm, bytes): if not wasm.startswith(b'\x00asm'): raise ValueError('run_wasm_in_node() given bytes do not look like a wasm module.') else: raise TypeError('run_wasm_in_node() expects a wasm module or bytes.') wasm_text = str(list(wasm)) # [0, 1, 12, ...] # Read templates src_filename_js = os.path.join(os.path.dirname(__file__), 'template.js') with open(src_filename_js, 'rb') as f: js = f.read().decode() # Produce JS js = js.replace('WASM_PLACEHOLDER', 'var wasm_data = new Uint8Array(' + wasm_text + ');') js += '\nprint_ln("Hello from Nodejs!");\ncompile_my_wasm();\n' # Write temporary file filename = os.path.join(tempfile.gettempdir(), 'pyscript_%i.js' % os.getpid()) with open(filename, 'wb') as f: f.write(js.encode()) # Execute JS in nodejs try: res = subprocess.check_output([get_node_exe(), '--use_strict', filename]) except Exception as err: if hasattr(err, 'output'): err = err.output.decode() else: err = str(err) err = err[:200] + '...' if len(err) > 200 else err raise Exception(err) finally: try: os.remove(filename) except Exception: pass print(res.decode().rstrip()) NODE_EXE = None def get_node_exe(): """ Small utility that provides the node exe. The first time this is called both 'nodejs' and 'node' are tried. To override the executable path, set the ``FLEXX_NODE_EXE`` environment variable. """ # This makes things work on Ubuntu's nodejs as well as other node # implementations, and allows users to set the node exe if necessary global NODE_EXE NODE_EXE = os.getenv('WASMFUN_NODE_EXE') or NODE_EXE if NODE_EXE is None: NODE_EXE = 'nodejs' try: subprocess.check_output([NODE_EXE, '-v']) except Exception: # pragma: no cover NODE_EXE = 'node' return NODE_EXE PKDJ$$wasmfun-0.1.dist-info/LICENSEBSD 2-Clause License Copyright (c) 2017, Almar Klein All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK!HuNRRwasmfun-0.1.dist-info/WHEEL1 0 RZq+D-Dv;_[*7Fp 6ՆKŵ|4|cBQaL2PK!H2qwasmfun-0.1.dist-info/METADATA=n0E8ee!QRTDY[#E=6}lfqܻ%`!E\0s bxU:xKaیCF?ZcFb53NB3IdXk%ۿf ~7۱].ͱl,l*5Xwk^Uxl"gՓ\PK!H^?}wasmfun-0.1.dist-info/RECORDuɎ@< :PvB (`"b&^36 |PSۆluC,):rϠ:AR@I93tNIe~&Z2RRpPk!fϚcٷI눠Xmdxnp|\NpYdcT>9[er[8S& u bÐˊp&iwmQ7y_Uei[8^M<%6d_-i>]~^U|^n{9>݅D+L!w@1/ƺzj lMТUlM=v%Ս5.k4KÓbao7-->fsޕ(8mCzYӘWot,-6v@~q{*T=`ulEu*oRS$h:{q8w*$x8uy$-T"(NTyUTsbN[2ʉٝIk#`W¡ف ː7O,'WfE] h4Qߕ'mQm(m ~PKJ[AAwasmfun/README.mdPKTJ5bϧpwasmfun/__init__.pyPKxJ9AHwasmfun/_opcodes.pyPKi`J>^^wasmfun/components.pyPK-VJV~  qwasmfun/generate_docs.pyPKeJGkk#{wasmfun/template.htmlPKeJ--|wasmfun/template.jsPKEfJwasmfun/util.pyPKDJ$$(wasmfun-0.1.dist-info/LICENSEPK!HuNRRwasmfun-0.1.dist-info/WHEELPK!H2qwasmfun-0.1.dist-info/METADATAPK!H^?}wasmfun-0.1.dist-info/RECORDPK 5