def ffi_prep_callback(tp, f): """(ffi-prep-callback callback-tp fn) Prepares a Pixie function for use as a c callback. callback-tp is a ffi callback type, fn is a pixie function (can be a closure, native fn or any object that implements -invoke. Returns a function pointer that can be passed to c and invoked as a callback.""" affirm(isinstance(tp, CFunctionType), u"First argument to ffi-prep-callback must be a CFunctionType") raw_closure = rffi.cast(rffi.VOIDP, clibffi.closureHeap.alloc()) if not we_are_translated(): unique_id = id_generator.get_next() else: unique_id = rffi.cast(lltype.Signed, raw_closure) res = clibffi.c_ffi_prep_closure( rffi.cast(clibffi.FFI_CLOSUREP, raw_closure), tp.get_cd().cif, invoke_callback, rffi.cast(rffi.VOIDP, unique_id)) if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK: registered_callbacks[unique_id] = None runtime_error(u"libffi failed to build this callback") cb = CCallback(tp, raw_closure, unique_id, f) registered_callbacks[unique_id] = cb return cb
def index_of3(a, sep, start): affirm(isinstance(start, Integer), u"Third argument must be an integer") start = start.int_val() if start >= 0: return rt.wrap(rt.name(a).find(rt.name(sep), start)) else: runtime_error(u"Third argument must be a non-negative integer")
def _invoke(self, args): arity = len(args) tp_arity = len(self._c_fn_type._arg_types) if self._c_fn_type._is_variadic: if arity < tp_arity: runtime_error(u"Wrong number of args to fn: got " + unicode(str(arity)) + u", expected at least " + unicode(str(tp_arity))) else: if arity != tp_arity: runtime_error(u"Wrong number of args to fn: got " + unicode(str(arity)) + u", expected " + unicode(str(tp_arity))) exb, tokens = self.prep_exb(args) cd = jit.promote(self._c_fn_type.get_cd()) #fp = jit.promote(self._f_ptr) jit_ffi_call(cd, self._f_ptr, exb) ret_val = self.get_ret_val_from_buffer(exb) for x in range(len(args)): t = tokens[x] if t is not None: t.finalize_token() lltype.free(exb, flavor="raw") keepalive_until_here(args) return ret_val
def ffi_prep_callback(tp, f): """(ffi-prep-callback callback-tp fn) Prepares a Pixie function for use as a c callback. callback-tp is a ffi callback type, fn is a pixie function (can be a closure, native fn or any object that implements -invoke. Returns a function pointer that can be passed to c and invoked as a callback.""" affirm(isinstance(tp, CFunctionType), u"First argument to ffi-prep-callback must be a CFunctionType") raw_closure = rffi.cast(rffi.VOIDP, clibffi.closureHeap.alloc()) if not we_are_translated(): unique_id = id_generator.get_next() else: unique_id = rffi.cast(lltype.Signed, raw_closure) res = clibffi.c_ffi_prep_closure(rffi.cast(clibffi.FFI_CLOSUREP, raw_closure), tp.get_cd().cif, invoke_callback, rffi.cast(rffi.VOIDP, unique_id)) if rffi.cast(lltype.Signed, res) != clibffi.FFI_OK: registered_callbacks[unique_id] = None runtime_error(u"libffi failed to build this callback") cb = CCallback(tp, raw_closure, unique_id, f) registered_callbacks[unique_id] = cb return cb
def invoke(self, args): if len(args) == self.get_arity(): return self.invoke_with(args, self) else: runtime_error(u"Invalid number of arguments " + unicode(str(len(args))) + u" for function '" + unicode(str(self._name)) + u"'. Expected " + unicode(str(self.get_arity())))
def c_free(frm): if not isinstance(frm, CStruct) and not isinstance(frm, VoidP): runtime_error(u"Can only free CStructs or CVoidP") lltype.free(frm.raw_data(), flavor="raw") return nil
def push_arg(self, idx): if not 0 <= idx < len(self.args): runtime_error(u"Invalid argument " + unicode(str(idx)) + u" function takes " + unicode(str(len(self.args))) + u" args") self.push(self.args[r_uint(idx)])
def push_arg(self, idx): if not 0 <= idx < len(self.args): runtime_error(u"Invalid number of arguments " + unicode(str(idx)) + u" for function '" + unicode(str(self.code_obj._name)) + u"'. Expected " + unicode(str(self.code_obj.get_arity()))) self.push(self.args[r_uint(idx)])
def nth(self, delta): affirm(delta >= 0, u"Invalid nth value, (compiler error)") if not self.sp - 1 >= delta: runtime_error(u"Interpreter nth out of range: " + unicode(str(self.sp - 1)) + u", " + unicode(str(delta))) return self.stack[self.sp - delta - 1]
def rawallocate(self): # compute the total size needed in the CIF_DESCRIPTION buffer self.nb_bytes = 0 self.bufferp = lltype.nullptr(rffi.CCHARP.TO) self.fb_build() # allocate the buffer if we_are_translated(): rawmem = lltype.malloc(rffi.CCHARP.TO, self.nb_bytes, flavor="raw") rawmem = rffi.cast(CIF_DESCRIPTION_P, rawmem) else: # gross overestimation of the length below, but too bad rawmem = lltype.malloc(CIF_DESCRIPTION_P.TO, self.nb_bytes, flavor="raw") # the buffer is automatically managed from the W_CTypeFunc instance # ctypefunc._cd = rawmem # call again fb_build() to really build the libffi data structures self.bufferp = rffi.cast(rffi.CCHARP, rawmem) self.fb_build() assert self.bufferp == rffi.ptradd(rffi.cast(rffi.CCHARP, rawmem), self.nb_bytes) # fill in the 'exchange_*' fields self.fb_build_exchange(rawmem) # fill in the extra fields self.fb_extra_fields(rawmem) # call libffi's ffi_prep_cif() function res = jit_libffi.jit_ffi_prep_cif(rawmem) if res != clibffi.FFI_OK: runtime_error(u"libffi failed to build function type") return rawmem
def struct_size(tp): """(struct-size tp) Gives the size of the given CStruct type tp, in bytes.""" if not isinstance(tp, CStructType): runtime_error(u"Expected a CStruct type to get the size of, got " + rt.name(rt.str(tp))) return rt.wrap(tp.get_size())
def _extend(proto_fn, tp, fn): if not isinstance(proto_fn, PolymorphicFn): runtime_error(u"Fist argument to extend should be a PolymorphicFn not a " + proto_fn.type().name()) affirm(isinstance(tp, Type) or isinstance(tp, Protocol), u"Second argument to extend must be a Type or Protocol") affirm(isinstance(fn, BaseCode), u"Last argument to extend must be a function") proto_fn.extend(tp, fn) return nil
def index_of4(a, sep, start, end): affirm(isinstance(start, Integer) and isinstance(end, Integer), u"Third and fourth argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a).find(rt.name(sep), start, end)) else: runtime_error(u"Third and fourth argument must be non-negative integers")
def c_cast(frm, to): if not isinstance(to, CStructType): runtime_error(u"Expected a CStruct type to cast to, got " + rt.name(rt.str(to))) if not isinstance(frm, CStruct) and not isinstance(frm, VoidP): runtime_error(u"From must be a CVoidP or a CStruct, got " + rt.name(rt.str(frm))) return to.cast_to(frm)
def wrapper(*args): ret = fn.invoke(py_list(args)) if ret is nil: return None if not isinstance(ret, String): from pixie.vm.object import runtime_error runtime_error(u"Invalid return value, expected String") return ret._str
def val_at(self, key, not_found): if not isinstance(key, String): runtime_error(u"Environment variables are strings ") key_str = str(rt.name(key)) try: var = os.environ[key_str] return rt.wrap(var) except KeyError: return not_found
def set_field(self, name, val): self._custom_type.set_mutable(name) idx = self._custom_type.get_slot_idx(name) if idx == -1: runtime_error(u"Invalid field named " + rt.name(rt.str(name)) + u" on type " + rt.name(rt.str(self.type()))) self._fields[idx] = val return self
def substring3(a, start, end): affirm(isinstance(a, String), u"First argument must be a string") affirm(isinstance(start, Integer) and isinstance(end, Integer), u"Second and third argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a)[start:end]) else: runtime_error(u"Second and third argument must be non-negative integers")
def get_field(self, name): idx = self._custom_type.get_slot_idx(name) if idx == -1: runtime_error(u"Invalid field named " + rt.name(rt.str(name)) + u" on type " + rt.name(rt.str(self.type()))) if self._custom_type.is_mutable(name): return self._fields[idx] else: return self.get_field_immutable(idx)
def to_float(x): if isinstance(x, Float): return x if isinstance(x, Ratio): return rt.wrap(x.numerator() / float(x.denominator())) if isinstance(x, Integer): return rt.wrap(float(x.int_val())) if isinstance(x, BigInteger): return rt.wrap(x.bigint_val().tofloat()) object.runtime_error(u"Cannot convert %s to float" %x.type().name())
def write_int_raw(i, wtr): #if 0 <= i <= SMALL_INT_MAX: # wtr.write(chr((i & 0xFF) + SMALL_INT_START)) if 0 <= i <= MAX_INT32: wtr.write(chr(i & 0xFF)) wtr.write(chr((i >> 8) & 0xFF)) wtr.write(chr((i >> 16) & 0xFF)) wtr.write(chr((i >> 24) & 0xFF)) else: runtime_error(u"Raw int must be less than MAX_INT32, got: " + unicode(str(i)))
def set_val(self, k, v): (tp, offset) = self._type.get_desc(k) if tp is None: runtime_error(u"Invalid field name: " + rt.name(rt.str(k))) offset = rffi.ptradd(self._buffer, offset) tp.ffi_set_value(rffi.cast(rffi.VOIDP, offset), v) return nil
def c_cast(frm, to): """(cast from to) Converts a VoidP to a CStruct. From is either a VoidP or a CStruct, to is a CStruct type.""" if not isinstance(to, CStructType): runtime_error(u"Expected a CStruct type to cast to, got " + rt.name(rt.str(to))) if not isinstance(frm, CStruct) and not isinstance(frm, VoidP): runtime_error(u"From must be a CVoidP or a CStruct, got " + rt.name(rt.str(frm))) return to.cast_to(frm)
def to_float(x): if isinstance(x, Float): return x if isinstance(x, Ratio): return rt.wrap(x.numerator() / float(x.denominator())) if isinstance(x, Integer): return rt.wrap(float(x.int_val())) if isinstance(x, BigInteger): return rt.wrap(x.bigint_val().tofloat()) object.runtime_error(u"Cannot convert %s to float" % x.type().name())
def pop(self): #print type(self.sp), self.sp self.sp -= 1 if not 0 <= self.sp < len(self.stack): runtime_error(u"Stack out of range: " + unicode(str(self.sp))) v = self.stack[self.sp] self.stack[self.sp] = None return v
def index_of4(a, sep, start, end): affirm( isinstance(start, Integer) and isinstance(end, Integer), u"Third and fourth argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a).find(rt.name(sep), start, end)) else: runtime_error( u"Third and fourth argument must be non-negative integers")
def assoc_at(self, idx, val): if idx >= 0 and idx < self._cnt: if idx >= self.tailoff(): new_tail = self._tail[:] new_tail[idx & 0x01f] = val return PersistentVector(self._meta, self._cnt, self._shift, self._root, new_tail) return PersistentVector(self._meta, self._cnt, self._shift, do_assoc(self._shift, self._root, idx, val), self._tail) if idx == self._cnt: return self.conj(val) else: object.runtime_error(u"index out of range")
def pop(self): #print type(self.sp), self.sp self.sp -= 1 if not 0 <= self.sp < len(self.stack): runtime_error(u"Stack out of range: " + unicode(str(self.sp)), u"pixie.vm.interpreter/InterpreterError") v = self.stack[self.sp] self.stack[self.sp] = None return v
def substring3(a, start, end): affirm(isinstance(a, String), u"First argument must be a string") affirm( isinstance(start, Integer) and isinstance(end, Integer), u"Second and third argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a)[start:end]) else: runtime_error( u"Second and third argument must be non-negative integers")
def set_field(self, name, val): idx = self._custom_type.get_slot_idx(name) if idx == -1: runtime_error(u"Invalid field named " + rt.name(rt.str(name)) + u" on type " + rt.name(rt.str(self.type())), u"pixie.stdlib/InvalidFieldException") old_val = self.get_field_by_idx(idx) if isinstance(old_val, AbstractMutableCell): old_val.set_mutable_cell_value(self._custom_type, self, name, idx, val) else: self._custom_type.set_mutable(name) self.set_field_by_idx(idx, val) return self
def _extend(proto_fn, tp, fn): if not isinstance(proto_fn, PolymorphicFn): runtime_error( u"Fist argument to extend should be a PolymorphicFn not a " + proto_fn.type().name()) affirm( isinstance(tp, Type) or isinstance(tp, Protocol), u"Second argument to extend must be a Type or Protocol") affirm(isinstance(fn, BaseCode), u"Last argument to extend must be a function") proto_fn.extend(tp, fn) return nil
def set_field(self, name, val): idx = self._custom_type.get_slot_idx(name) if idx == -1: runtime_error(u"Invalid field named " + rt.name(rt.str(name)) + u" on type " + rt.name(rt.str(self.type()))) old_val = self._fields[idx] if isinstance(old_val, AbstractMutableCell): old_val.set_mutable_cell_value(self._custom_type, self._fields, name, idx, val) else: self._custom_type.set_mutable(name) self._fields[idx] = val return self
def get_field(self, name): idx = self._custom_type.get_slot_idx(name) if idx == -1: runtime_error(u"Invalid field named " + rt.name(rt.str(name)) + u" on type " + rt.name(rt.str(self.type())), u"pixie.stdlib/InvalidFieldException") if self._custom_type.is_mutable(name): value = self.get_field_by_idx(idx) else: value = self.get_field_immutable(idx) if isinstance(value, AbstractMutableCell): return value.get_mutable_cell_value() else: return value
def c_struct(name, size, spec): d = {} for x in range(rt.count(spec)): row = rt.nth(spec, rt.wrap(x)) nm = rt.nth(row, rt.wrap(0)) tp = rt.nth(row, rt.wrap(1)) offset = rt.nth(row, rt.wrap(2)) affirm(isinstance(nm, Keyword), u"c-struct field names must be keywords") if not isinstance(tp, CType): runtime_error(u"c-struct field types must be c types, got: " + rt.name(rt.str(tp))) d[nm] = (tp, offset.int_val()) return CStructType(rt.name(name), size.int_val(), d)
def ffi_callback(args, ret_type): """(ffi-callback args ret-type) Creates a ffi callback type. Args is a vector of CType args. Ret-type is the CType return type of the callback. Returns a ffi callback type that can be used with ffi-prep-callback.""" args_w = [None] * rt.count(args) for x in range(rt.count(args)): arg = rt.nth(args, rt.wrap(x)) if not isinstance(arg, object.Type): runtime_error(u"Expected type, got " + rt.name(rt.str(arg))) args_w[x] = arg if not isinstance(ret_type, object.Type): runtime_error(u"Expected type, got " + rt.name(rt.str(ret_type))) return CFunctionType(args_w, ret_type)
def load_lib(self): if not self._is_inited: load_paths = rt.deref(rt.deref(rt.load_paths)) for x in range(rt.count(load_paths)): s = rffi.str2charp(str(rt.name(rt.nth(load_paths, rt.wrap(x)))) + "/" + str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: continue finally: rffi.free_charp(s) break if not self._is_inited: s = rffi.str2charp(str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: raise object.runtime_error(u"Couldn't Load Library: " + self._name, u"pixie.stdlib/LibraryNotFoundException") finally: rffi.free_charp(s)
def syntax_quote(form): if isinstance(form, Symbol) and compiler.is_compiler_special(form): ret = rt.list(QUOTE, form) elif isinstance(form, Symbol): if rt.namespace(form) is None and rt.name(form).endswith("#"): gmap = rt.deref(GEN_SYM_ENV) affirm(gmap is not nil, u"Gensym literal used outside a syntax quote") gs = rt.get(gmap, form) if gs is nil: gs = rt.symbol(rt.str(form, rt.wrap(u"__"), rt.gensym())) GEN_SYM_ENV.set_value(rt.assoc(gmap, form, gs)) form = gs else: var = rt.resolve_in(compiler.NS_VAR.deref(), form) if var is nil: form = rt.symbol(rt.str(rt.wrap(rt.name(rt.deref(compiler.NS_VAR))), rt.wrap(u"/"), form)) else: form = rt.symbol(rt.str(rt.wrap(rt.namespace(var)), rt.wrap(u"/"), rt.str(rt.wrap(rt.name(var))))) ret = rt.list(QUOTE, form) elif is_unquote(form): ret = rt.first(rt.next(form)) elif is_unquote_splicing(form): return runtime_error(u"Unquote splicing not used inside list") elif rt.vector_QMARK_(form) is true: ret = rt.list(APPLY, CONCAT, SyntaxQuoteReader.expand_list(form)) elif rt.map_QMARK_(form) is true: mes = SyntaxQuoteReader.flatten_map(form) ret = rt.list(APPLY, HASHMAP, rt.list(APPLY, CONCAT, SyntaxQuoteReader.expand_list(mes))) elif form is not nil and rt.seq_QMARK_(form) is true: ret = rt.list(APPLY, LIST, rt.cons(CONCAT, SyntaxQuoteReader.expand_list(rt.seq(form)))) else: ret = rt.list(QUOTE, form) return ret
def load_lib(self): if not self._is_inited: load_paths = rt.deref(rt.deref(rt.load_paths)) for x in range(rt.count(load_paths)): s = rffi.str2charp( str(rt.name(rt.nth(load_paths, rt.wrap(x)))) + "/" + str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: continue finally: rffi.free_charp(s) break if not self._is_inited: s = rffi.str2charp(str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: raise object.runtime_error( u"Couldn't Load Library: " + self._name, u"pixie.stdlib/LibraryNotFoundException") finally: rffi.free_charp(s)
def write_object(obj, wtr): wtr.flush() if isinstance(obj, String): write_string(rt.name(obj), wtr) elif isinstance(obj, Integer): write_int(obj.int_val(), wtr) elif isinstance(obj, BigInteger): #TODO test write_bigint(obj.bigint_val(), wtr) elif isinstance(obj, Float): write_float(obj.float_val(), wtr) elif isinstance(obj, Code): write_code(obj, wtr) elif obj is nil: wtr.write(chr(NIL)) elif isinstance(obj, Var): #wtr.write_cached_obj(obj, write_var) write_var(obj, wtr) elif rt._satisfies_QMARK_(rt.IMap.deref(), obj): write_map(obj, wtr) elif rt._satisfies_QMARK_(rt.IVector.deref(), obj): write_vector(obj, wtr) elif rt._satisfies_QMARK_(rt.ISeq.deref(), obj): write_seq(obj, wtr) elif isinstance(obj, Keyword): wtr.write_cached_obj(obj, write_keyword) elif isinstance(obj, LinePromise): wtr.write_cached_obj(obj, write_line_promise) elif obj is true: write_tag(TRUE, wtr) elif obj is false: write_tag(FALSE, wtr) elif isinstance(obj, Symbol): write_symbol(obj, wtr) elif isinstance(obj, Namespace): wtr.write_cached_obj(obj, write_namespace) elif isinstance(obj, InterpreterCodeInfo): wtr.write_cached_obj(obj, write_interpreter_code_info) else: from pixie.vm.libs.pxic.util import write_handlers handler = write_handlers.get(obj.type(), None) if handler is None: runtime_error(u"Object is not supported by pxic writer: " + rt.name(rt.str(obj.type()))) else: write_tag(TAGGED, wtr) write_string_raw(obj.type().name(), wtr) write_object(handler.invoke([obj]), wtr)
def get_fn(self, arity): f = self._arities.get(arity, None) if f is not None: return f if self._rest_fn is not None and arity >= self._required_arity: return self._rest_fn acc = [] sorted = TimSort(self.get_arities()) sorted.sort() for x in sorted.list: acc.append(unicode(str(x))) if self._rest_fn: acc.append(unicode(str(self._rest_fn.required_arity())) + u"+") runtime_error(u"Wrong number of arguments " + unicode(str(arity)) + u" for function '" + unicode(self._name) + u"'. Expected " + join_last(acc, u"or"))
def read_inner(rdr, error_on_eof, always_return_form=True): try: eat_whitespace(rdr) except EOFError as ex: if error_on_eof: runtime_error(u"Unexpected EOF while reading", u"pixie.stdlib/EOFWhileReadingException") return eof ch = rdr.read() if isinstance(rdr, MetaDataReader): meta = rdr.get_metadata() else: meta = nil macro = handlers.get(ch, None) if macro is not None: itm = macro.invoke(rdr, ch) if always_return_form and itm == rdr: return read_inner(rdr, error_on_eof, always_return_form=always_return_form) elif is_digit(ch): itm = read_number(rdr, ch) elif ch == u"-": ch2 = rdr.read() if is_digit(ch2): rdr.unread() itm = read_number(rdr, ch) else: rdr.unread() itm = read_symbol(rdr, ch) else: itm = read_symbol(rdr, ch) if itm != rdr: if rt.has_meta_QMARK_(itm): itm = rt.with_meta(itm, rt.merge(meta, rt.meta(itm))) return itm
def get_fn(self, arity): f = self._arities.get(arity, None) if f is not None: return f if self._rest_fn is not None and arity >= self._required_arity: return self._rest_fn acc = [] sorted = TimSort(self.get_arities()) sorted.sort() for x in sorted.list: acc.append(unicode(str(x))) if self._rest_fn: acc.append(unicode(str(self._rest_fn.required_arity())) + u"+") runtime_error(u"Wrong number of arguments " + unicode(str(arity)) + u" for function '" + unicode(self._name) + u"'. Expected " + join_last(acc, u"or"), u"pixie.stdlib/InvalidArityException")