#include "pxt.h" #include MicroBit uBit; namespace pxt { int incr(uint32_t e) { if (e) { if (hasVTable(e)) ((RefObject*)e)->ref(); else ((RefCounted*)e)->incr(); } return e; } void decr(uint32_t e) { if (e) { if (hasVTable(e)) ((RefObject*)e)->unref(); else ((RefCounted*)e)->decr(); } } Action mkAction(int reflen, int totallen, int startptr) { check(0 <= reflen && reflen <= totallen, ERR_SIZE, 1); check(reflen <= totallen && totallen <= 255, ERR_SIZE, 2); check(bytecode[startptr] == 0xffff, ERR_INVALID_BINARY_HEADER, 3); check(bytecode[startptr + 1] == 0, ERR_INVALID_BINARY_HEADER, 4); uint32_t tmp = (uint32_t)&bytecode[startptr]; if (totallen == 0) { return tmp; // no closure needed } void *ptr = ::operator new(sizeof(RefAction) + totallen * sizeof(uint32_t)); RefAction *r = new (ptr) RefAction(); r->len = totallen; r->reflen = reflen; r->func = (ActionCB)((tmp + 4) | 1); memset(r->fields, 0, r->len * sizeof(uint32_t)); return (Action)r; } uint32_t runAction3(Action a, int arg0, int arg1, int arg2) { if (hasVTable(a)) return ((RefAction*)a)->runCore(arg0, arg1, arg2); else { check(*(uint16_t*)a == 0xffff, ERR_INVALID_BINARY_HEADER, 4); return ((ActionCB)((a + 4) | 1))(NULL, arg0, arg1, arg2); } } uint32_t runAction2(Action a, int arg0, int arg1) { return runAction3(a, arg0, arg1, 0); } uint32_t runAction1(Action a, int arg0) { return runAction3(a, arg0, 0, 0); } uint32_t runAction0(Action a) { return runAction3(a, 0, 0, 0); } RefRecord* mkClassInstance(int vtableOffset) { VTable *vtable = (VTable*)&bytecode[vtableOffset]; intcheck(vtable->methods[0] == &RefRecord_destroy, ERR_SIZE, 3); intcheck(vtable->methods[1] == &RefRecord_print, ERR_SIZE, 4); void *ptr = ::operator new(vtable->numbytes); RefRecord *r = new (ptr) RefRecord(PXT_VTABLE_TO_INT(vtable)); memset(r->fields, 0, vtable->numbytes - sizeof(RefRecord)); return r; } uint32_t RefRecord::ld(int idx) { //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 1); return fields[idx]; } uint32_t RefRecord::ldref(int idx) { //printf("LD %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 2); uint32_t tmp = fields[idx]; incr(tmp); return tmp; } void RefRecord::st(int idx, uint32_t v) { //intcheck((reflen == 255 ? 0 : reflen) <= idx && idx < len, ERR_OUT_OF_BOUNDS, 3); fields[idx] = v; } void RefRecord::stref(int idx, uint32_t v) { //printf("ST %p len=%d reflen=%d idx=%d\n", this, len, reflen, idx); //intcheck(0 <= idx && idx < reflen, ERR_OUT_OF_BOUNDS, 4); decr(fields[idx]); fields[idx] = v; } void RefObject::destroy() { ((RefObjectMethod)getVTable()->methods[0])(this); delete this; } void RefObject::print() { ((RefObjectMethod)getVTable()->methods[1])(this); } void RefRecord_destroy(RefRecord *r) { auto tbl = r->getVTable(); uint8_t *refmask = (uint8_t*)&tbl->methods[tbl->userdata & 0xff]; int len = (tbl->numbytes >> 2) - 1; for (int i = 0; i < len; ++i) { if (refmask[i]) decr(r->fields[i]); r->fields[i] = 0; } //RefRecord is allocated using placement new r->~RefRecord(); ::operator delete(r); } void RefRecord_print(RefRecord *r) { printf("RefRecord %p r=%d size=%d bytes\n", r, r->refcnt, r->getVTable()->numbytes); } void RefCollection::push(uint32_t x) { if (isRef()) incr(x); data.push_back(x); } uint32_t RefCollection::getAt(int x) { if (in_range(x)) { uint32_t tmp = data.at(x); if (isRef()) incr(tmp); return tmp; } else { error(ERR_OUT_OF_BOUNDS); return 0; } } void RefCollection::removeAt(int x) { if (!in_range(x)) return; if (isRef()) decr(data.at(x)); data.erase(data.begin()+x); } void RefCollection::setAt(int x, uint32_t y) { if (!in_range(x)) return; if (isRef()) { decr(data.at(x)); incr(y); } data.at(x) = y; } int RefCollection::indexOf(uint32_t x, int start) { if (!in_range(start)) return -1; if (isString()) { StringData *xx = (StringData*)x; for (uint32_t i = start; i < data.size(); ++i) { StringData *ee = (StringData*)data.at(i); if (xx->len == ee->len && memcmp(xx->data, ee->data, xx->len) == 0) return (int)i; } } else { for (uint32_t i = start; i < data.size(); ++i) if (data.at(i) == x) return (int)i; } return -1; } int RefCollection::removeElement(uint32_t x) { int idx = indexOf(x, 0); if (idx >= 0) { removeAt(idx); return 1; } return 0; } namespace Coll0 { PXT_VTABLE_BEGIN(RefCollection, 0, 0) PXT_VTABLE_END } namespace Coll1 { PXT_VTABLE_BEGIN(RefCollection, 1, 0) PXT_VTABLE_END } namespace Coll3 { PXT_VTABLE_BEGIN(RefCollection, 3, 0) PXT_VTABLE_END } RefCollection::RefCollection(uint16_t flags) : RefObject(0) { switch (flags) { case 0: vtable = PXT_VTABLE_TO_INT(&Coll0::RefCollection_vtable); break; case 1: vtable = PXT_VTABLE_TO_INT(&Coll1::RefCollection_vtable); break; case 3: vtable = PXT_VTABLE_TO_INT(&Coll3::RefCollection_vtable); break; default: error(ERR_SIZE); break; } } void RefCollection::destroy() { if (this->isRef()) for (uint32_t i = 0; i < this->data.size(); ++i) { decr(this->data[i]); this->data[i] = 0; } this->data.resize(0); } void RefCollection::print() { printf("RefCollection %p r=%d flags=%d size=%d [%p, ...]\n", this, refcnt, getFlags(), data.size(), data.size() > 0 ? data[0] : 0); } PXT_VTABLE_CTOR(RefAction) {} // fields[] contain captured locals void RefAction::destroy() { for (int i = 0; i < this->reflen; ++i) { decr(fields[i]); fields[i] = 0; } //RefAction is allocated using placement new this->~RefAction(); ::operator delete(this); } void RefAction::print() { printf("RefAction %p r=%d pc=0x%lx size=%d (%d refs)\n", this, refcnt, (const uint8_t*)func - (const uint8_t*)bytecode, len, reflen); } void RefLocal::print() { printf("RefLocal %p r=%d v=%d\n", this, refcnt, v); } void RefLocal::destroy() { } PXT_VTABLE_CTOR(RefLocal) { v = 0; } PXT_VTABLE_CTOR(RefRefLocal) { v = 0; } void RefRefLocal::print() { printf("RefRefLocal %p r=%d v=%p\n", this, refcnt, (void*)v); } void RefRefLocal::destroy() { decr(v); } PXT_VTABLE_BEGIN(RefMap, 0, RefMapMarker) PXT_VTABLE_END RefMap::RefMap() : PXT_VTABLE_INIT(RefMap) {} void RefMap::destroy() { for (unsigned i = 0; i < data.size(); ++i) { if (data[i].key & 1) { decr(data[i].val); } data[i].val = 0; } data.resize(0); } int RefMap::findIdx(uint32_t key) { for (unsigned i = 0; i < data.size(); ++i) { if (data[i].key >> 1 == key) return i; } return -1; } void RefMap::print() { printf("RefMap %p r=%d size=%d\n", this, refcnt, data.size()); } #ifdef DEBUG_MEMLEAKS std::set allptrs; void debugMemLeaks() { printf("LIVE POINTERS:\n"); for(std::set::iterator itr = allptrs.begin();itr!=allptrs.end();itr++) { (*itr)->print(); } printf("\n"); } #else void debugMemLeaks() {} #endif // --------------------------------------------------------------------------- // An adapter for the API expected by the run-time. // --------------------------------------------------------------------------- map, Action> handlersMap; MicroBitEvent lastEvent; // We have the invariant that if [dispatchEvent] is registered against the DAL // for a given event, then [handlersMap] contains a valid entry for that // event. void dispatchEvent(MicroBitEvent e) { lastEvent = e; Action curr = handlersMap[{ e.source, e.value }]; if (curr) runAction1(curr, e.value); curr = handlersMap[{ e.source, MICROBIT_EVT_ANY }]; if (curr) runAction1(curr, e.value); } void registerWithDal(int id, int event, Action a) { Action prev = handlersMap[{ id, event }]; if (prev) decr(prev); else uBit.messageBus.listen(id, event, dispatchEvent); incr(a); handlersMap[{ id, event }] = a; } void fiberDone(void *a) { decr((Action)a); release_fiber(); } void runInBackground(Action a) { if (a != 0) { incr(a); create_fiber((void(*)(void*))runAction0, (void*)a, fiberDone); } } void error(ERROR code, int subcode) { printf("Error: %d [%d]\n", code, subcode); uBit.panic(42); } uint16_t *bytecode; uint32_t *globals; int numGlobals; uint32_t *allocate(uint16_t sz) { uint32_t *arr = new uint32_t[sz]; memset(arr, 0, sz * 4); return arr; } void checkStr(bool cond, const char *msg) { if (!cond) { while (true) { uBit.display.scroll(msg, 100); uBit.sleep(100); } } } int templateHash() { return ((int*)bytecode)[4]; } int programHash() { return ((int*)bytecode)[6]; } int getNumGlobals() { return bytecode[16]; } void exec_binary(int32_t *pc) { // XXX re-enable once the calibration code is fixed and [editor/embedded.ts] // properly prepends a call to [internal_main]. // ::touch_develop::internal_main(); // unique group for radio based on source hash // ::touch_develop::micro_bit::radioDefaultGroup = programHash(); // repeat error 4 times and restart as needed microbit_panic_timeout(4); int32_t ver = *pc++; checkStr(ver == 0x4209, ":( Bad runtime version"); bytecode = *((uint16_t**)pc++); // the actual bytecode is here globals = allocate(getNumGlobals()); // just compare the first word checkStr(((uint32_t*)bytecode)[0] == 0x923B8E70 && templateHash() == *pc, ":( Failed partial flash"); uint32_t startptr = (uint32_t)bytecode; startptr += 48; // header startptr |= 1; // Thumb state ((uint32_t (*)())startptr)(); #ifdef DEBUG_MEMLEAKS pxt::debugMemLeaks(); #endif return; } void start() { exec_binary((int32_t*)functionsAndBytecode); } } // vim: ts=2 sw=2 expandtab