void CloudThermal::Save(Serialiser &s) const { s.Write8(1); s.Write64(client_key); s << time; s.WriteT(Pack()); }
void Scene3700::Viewer::synchronise(Serialiser &s) { SceneObject::synchronise(s); s.syncAsByte(_active); s.syncAsSint16LE(_countdownCtr); for (int idx = 0; idx < 4; ++idx) { s.syncAsSint16LE(_frameList[idx]); s.syncAsSint16LE(_percentList[idx]); } }
void CloudData::Save(Serialiser &s) const { s.Write32(CLOUD_MAGIC); s.Write32(CLOUD_VERSION); clients.Save(s); s.Write8(1); thermals.Save(s); s.Write8(0); }
void CloudThermalContainer::Save(Serialiser &s) const { s.Write8(1); for (const auto &thermal : list) { s.Write8(1); thermal.Save(s); } s.Write8(0); s.Write8(0); }
RemoteRenderer(Network::Socket *sock) : m_Socket(sock) { map<RDCDriver,wstring> m = RenderDoc::Inst().GetReplayDrivers(); m_Proxies.reserve(m.size()); for(auto it=m.begin(); it != m.end(); ++it) m_Proxies.push_back(*it); { PacketType type; Serialiser *ser = NULL; GetPacket(type, &ser); m.clear(); if(ser) { uint32_t count = 0; ser->Serialise("", count); for(uint32_t i=0; i < count; i++) { RDCDriver driver = RDC_Unknown; wstring name = L""; ser->Serialise("", driver); ser->Serialise("", name); m[driver] = name; } delete ser; } } m_RemoteDrivers.reserve(m.size()); for(auto it=m.begin(); it != m.end(); ++it) m_RemoteDrivers.push_back(*it); m_ProxyDriver = NULL; m_ProxySerialiser = NULL; }
TargetControl(Network::Socket *sock, string clientName, bool forceConnection, bool localhost) : m_Socket(sock), m_Local(localhost) { PacketType type; vector<byte> payload; m_PID = 0; { Serialiser ser("", Serialiser::WRITING, false); ser.SerialiseString("", clientName); ser.Serialise("", forceConnection); if(!SendPacket(m_Socket, ePacket_Handshake, ser)) { SAFE_DELETE(m_Socket); return; } } Serialiser *ser = NULL; GetPacket(type, ser); // failed handshaking if(m_Socket == NULL || ser == NULL) return; RDCASSERT(type == ePacket_Handshake || type == ePacket_Busy); if(type == ePacket_Handshake) { ser->Serialise("", m_Target); ser->Serialise("", m_API); ser->Serialise("", m_PID); RDCLOG("Got remote handshake: %s (%s) [%u]", m_Target.c_str(), m_API.c_str(), m_PID); } else if(type == ePacket_Busy) { ser->Serialise("", m_Target); ser->Serialise("", m_API); ser->Serialise("", m_BusyClient); RDCLOG("Got remote busy signal: %s (%s) owned by %s", m_Target.c_str(), m_API.c_str(), m_BusyClient.c_str()); } SAFE_DELETE(ser); }
ReplayCreateStatus CreateProxyRenderer(uint32_t proxyid, const char *logfile, float *progress, ReplayRenderer **rend) { if(rend == NULL) return eReplayCreate_InternalError; if(proxyid >= m_Proxies.size()) { RDCERR("Invalid proxy driver id %d specified for remote renderer", proxyid); return eReplayCreate_InternalError; } float dummy = 0.0f; if(progress == NULL) progress = &dummy; RDCDriver proxydrivertype = m_Proxies[proxyid].first; Serialiser ser("", Serialiser::WRITING, false); if(!SendChunkedFile(m_Socket, ePacket_CopyCapture, logfile, ser, progress)) { SAFE_DELETE(m_Socket); return eReplayCreate_NetworkIOFailed; } RDCLOG("Sent file to replay host. Loading..."); PacketType type = ePacket_Noop; while(m_Socket) { Serialiser *progressSer; GetPacket(type, &progressSer); if(!m_Socket || type != ePacket_LogOpenProgress) break; progressSer->Serialise("", *progress); RDCLOG("% 3.0f%%...", (*progress)*100.0f); } if(!m_Socket || type != ePacket_LogReady) return eReplayCreate_NetworkIOFailed; *progress = 1.0f; RDCLOG("Log ready on replay host"); IReplayDriver *proxyDriver = NULL; auto status = RenderDoc::Inst().CreateReplayDriver(proxydrivertype, NULL, &proxyDriver); if(status != eReplayCreate_Success || !proxyDriver) { if(proxyDriver) proxyDriver->Shutdown(); return status; } ReplayRenderer *ret = new ReplayRenderer(); ProxySerialiser *proxy = new ProxySerialiser(m_Socket, proxyDriver); status = ret->SetDevice(proxy); if(status != eReplayCreate_Success) { SAFE_DELETE(ret); return status; } // ReplayRenderer takes ownership of the ProxySerialiser (as IReplayDriver) // and it cleans itself up in Shutdown. *rend = ret; return eReplayCreate_Success; }
void ReceiveMessage(RemoteMessage *msg) { if(m_Socket == NULL) { msg->Type = eRemoteMsg_Disconnected; return; } if(!m_Socket->IsRecvDataWaiting()) { if(!m_Socket->Connected()) { SAFE_DELETE(m_Socket); msg->Type = eRemoteMsg_Disconnected; } else { Threading::Sleep(2); msg->Type = eRemoteMsg_Noop; } return; } PacketType type; Serialiser *ser = NULL; GetPacket(type, ser); if(m_Socket == NULL) { SAFE_DELETE(ser); msg->Type = eRemoteMsg_Disconnected; return; } else { if(type == ePacket_Noop) { SAFE_DELETE(ser); msg->Type = eRemoteMsg_Noop; return; } else if(type == ePacket_Busy) { string existingClient; ser->Serialise("", existingClient); SAFE_DELETE(ser); SAFE_DELETE(m_Socket); RDCLOG("Got busy signal: '%s", existingClient.c_str()); msg->Type = eRemoteMsg_Busy; msg->Busy.ClientName = existingClient; return; } else if(type == ePacket_CopyCapture) { msg->Type = eRemoteMsg_CaptureCopied; ser->Serialise("", msg->NewCapture.ID); SAFE_DELETE(ser); msg->NewCapture.localpath = m_CaptureCopies[msg->NewCapture.ID]; if(!RecvChunkedFile(m_Socket, ePacket_CopyCapture, msg->NewCapture.localpath.elems, ser, NULL)) { SAFE_DELETE(ser); SAFE_DELETE(m_Socket); msg->Type = eRemoteMsg_Disconnected; return; } m_CaptureCopies.erase(msg->NewCapture.ID); SAFE_DELETE(ser); return; } else if(type == ePacket_NewChild) { msg->Type = eRemoteMsg_NewChild; ser->Serialise("", msg->NewChild.PID); ser->Serialise("", msg->NewChild.ident); RDCLOG("Got a new child process: %u %u", msg->NewChild.PID, msg->NewChild.ident); SAFE_DELETE(ser); return; } else if(type == ePacket_NewCapture) { msg->Type = eRemoteMsg_NewCapture; ser->Serialise("", msg->NewCapture.ID); ser->Serialise("", msg->NewCapture.timestamp); string path; ser->Serialise("", path); msg->NewCapture.localpath = path; if(!m_Local) msg->NewCapture.localpath = ""; uint32_t thumblen = 0; ser->Serialise("", thumblen); create_array_uninit(msg->NewCapture.thumbnail, thumblen); size_t l = 0; byte *buf = &msg->NewCapture.thumbnail[0]; ser->SerialiseBuffer("", buf, l); RDCLOG("Got a new capture: %d (time %llu) %d byte thumbnail", msg->NewCapture.ID, msg->NewCapture.timestamp, thumblen); SAFE_DELETE(ser); return; } else if(type == ePacket_RegisterAPI) { msg->Type = eRemoteMsg_RegisterAPI; ser->Serialise("", m_API); msg->RegisterAPI.APIName = m_API; RDCLOG("Used API: %s", m_API.c_str()); SAFE_DELETE(ser); return; } } SAFE_DELETE(ser); msg->Type = eRemoteMsg_Noop; }
void RenderDoc::RemoteAccessClientThread(void *s) { Threading::KeepModuleAlive(); Network::Socket *client = (Network::Socket *)s; Serialiser ser("", Serialiser::WRITING, false); string api = ""; RDCDriver driver; RenderDoc::Inst().GetCurrentDriver(driver, api); ser.Rewind(); string target = RenderDoc::Inst().GetCurrentTarget(); ser.Serialise("", target); ser.Serialise("", api); uint32_t mypid = Process::GetCurrentPID(); ser.Serialise("", mypid); if(!SendPacket(client, ePacket_Handshake, ser)) { SAFE_DELETE(client); { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = ""; } Threading::ReleaseModuleExitThread(); return; } const int pingtime = 1000; // ping every 1000ms const int ticktime = 10; // tick every 10ms int curtime = 0; vector<CaptureData> captures; vector<pair<uint32_t, uint32_t> > children; while(client) { if(RenderDoc::Inst().m_RemoteClientThreadShutdown || (client && !client->Connected())) { SAFE_DELETE(client); break; } ser.Rewind(); Threading::Sleep(ticktime); curtime += ticktime; PacketType packetType = ePacket_Noop; string curapi; RenderDoc::Inst().GetCurrentDriver(driver, curapi); vector<CaptureData> caps = RenderDoc::Inst().GetCaptures(); vector<pair<uint32_t, uint32_t> > childprocs = RenderDoc::Inst().GetChildProcesses(); if(curapi != api) { api = curapi; ser.Serialise("", api); packetType = ePacket_RegisterAPI; } else if(caps.size() != captures.size()) { uint32_t idx = (uint32_t)captures.size(); captures.push_back(caps[idx]); packetType = ePacket_NewCapture; std::string path = FileIO::GetFullPathname(captures.back().path); ser.Serialise("", idx); ser.Serialise("", captures.back().timestamp); ser.Serialise("", path); uint32_t len = 0; RENDERDOC_GetThumbnail(captures.back().path.c_str(), NULL, len); byte *thumb = new byte[len]; RENDERDOC_GetThumbnail(captures.back().path.c_str(), thumb, len); size_t l = len; ser.Serialise("", len); ser.SerialiseBuffer("", thumb, l); delete[] thumb; } else if(childprocs.size() != children.size()) { uint32_t idx = (uint32_t)children.size(); children.push_back(childprocs[idx]); packetType = ePacket_NewChild; ser.Serialise("", children.back().first); ser.Serialise("", children.back().second); } if(curtime < pingtime && packetType == ePacket_Noop) { if(client->IsRecvDataWaiting()) { PacketType type; Serialiser *recvser = NULL; if(!RecvPacket(client, type, &recvser)) SAFE_DELETE(client); if(client == NULL) { SAFE_DELETE(recvser); continue; } else if(type == ePacket_TriggerCapture) { RenderDoc::Inst().TriggerCapture(); } else if(type == ePacket_QueueCapture) { uint32_t frameNum = 0; recvser->Serialise("", frameNum); RenderDoc::Inst().QueueCapture(frameNum); } else if(type == ePacket_CopyCapture) { caps = RenderDoc::Inst().GetCaptures(); uint32_t id = 0; recvser->Serialise("", id); if(id < caps.size()) { ser.Serialise("", id); if(!SendPacket(client, ePacket_CopyCapture, ser)) { SAFE_DELETE(client); continue; } ser.Rewind(); if(!SendChunkedFile(client, ePacket_CopyCapture, caps[id].path.c_str(), ser, NULL)) { SAFE_DELETE(client); continue; } RenderDoc::Inst().MarkCaptureRetrieved(id); } } SAFE_DELETE(recvser); } continue; } curtime = 0; if(!SendPacket(client, packetType, ser)) { SAFE_DELETE(client); continue; } } // give up our connection { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = ""; } Threading::ReleaseModuleExitThread(); }
void RenderDoc::RemoteAccessServerThread(void *s) { Threading::KeepModuleAlive(); Network::Socket *sock = (Network::Socket *)s; RenderDoc::Inst().m_SingleClientName = ""; Threading::ThreadHandle clientThread = 0; RenderDoc::Inst().m_RemoteClientThreadShutdown = false; while(!RenderDoc::Inst().m_RemoteServerThreadShutdown) { Network::Socket *client = sock->AcceptClient(false); if(client == NULL) { if(!sock->Connected()) { RDCERR("Error in accept - shutting down server"); SAFE_DELETE(sock); Threading::ReleaseModuleExitThread(); return; } Threading::Sleep(5); continue; } string existingClient; string newClient; bool kick = false; // receive handshake from client and get its name { PacketType type; Serialiser *ser = NULL; if(!RecvPacket(client, type, &ser)) { SAFE_DELETE(ser); SAFE_DELETE(client); continue; } if(type != ePacket_Handshake) { SAFE_DELETE(ser); SAFE_DELETE(client); continue; } ser->SerialiseString("", newClient); ser->Serialise("", kick); SAFE_DELETE(ser); if(newClient.empty()) { SAFE_DELETE(client); continue; } } // see if we have a client { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); existingClient = RenderDoc::Inst().m_SingleClientName; } if(!existingClient.empty() && kick) { // forcibly close communication thread which will kill the connection RenderDoc::Inst().m_RemoteClientThreadShutdown = true; Threading::JoinThread(clientThread); Threading::CloseThread(clientThread); clientThread = 0; RenderDoc::Inst().m_RemoteClientThreadShutdown = false; existingClient = ""; } if(existingClient.empty()) { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = newClient; } // if we've claimed client status, spawn a thread to communicate if(existingClient.empty() || kick) { clientThread = Threading::CreateThread(RemoteAccessClientThread, client); continue; } else { // if we've been asked to kick the existing connection off // reject this connection and tell them who is busy Serialiser ser("", Serialiser::WRITING, false); string api = ""; RDCDriver driver; RenderDoc::Inst().GetCurrentDriver(driver, api); string target = RenderDoc::Inst().GetCurrentTarget(); ser.Serialise("", target); ser.Serialise("", api); ser.SerialiseString("", RenderDoc::Inst().m_SingleClientName); // don't care about errors, we're going to close the connection either way SendPacket(client, ePacket_Busy, ser); SAFE_DELETE(client); } } RenderDoc::Inst().m_RemoteClientThreadShutdown = true; // don't join, just close the thread, as we can't wait while in the middle of module unloading Threading::CloseThread(clientThread); clientThread = 0; Threading::ReleaseModuleExitThread(); }
void SoundManager::listenerSynchronise(Serialiser &s) { s.validate("SoundManager"); warning("TODO: SoundManager listenerSynchronise"); }
void RenderDoc::RemoteAccessClientThread(void *s) { Network::Socket *client = (Network::Socket *)s; Serialiser ser(L"", Serialiser::WRITING, false); wstring api = L""; RDCDriver driver; RenderDoc::Inst().GetCurrentDriver(driver, api); ser.Rewind(); wstring target = RenderDoc::Inst().GetCurrentTarget(); ser.Serialise("", target); ser.Serialise("", api); if(!SendPacket(client, ePacket_Handshake, ser)) { SAFE_DELETE(client); { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = L""; } return; } const int pingtime = 1000; // ping every 1000ms const int ticktime = 10; // tick every 10ms int curtime = 0; vector<wstring> captures; while(client) { if(RenderDoc::Inst().m_RemoteClientThreadShutdown || (client && !client->Connected())) { SAFE_DELETE(client); break; } ser.Rewind(); Threading::Sleep(ticktime); curtime += ticktime; PacketType packetType = ePacket_Noop; wstring curapi; RenderDoc::Inst().GetCurrentDriver(driver, curapi); if(curapi != api) { api = curapi; ser.Serialise("", api); packetType = ePacket_RegisterAPI; } else { vector<wstring> caps = RenderDoc::Inst().GetCaptures(); if(caps.size() != captures.size()) { uint32_t idx = (uint32_t)captures.size(); captures.push_back(caps[idx]); packetType = ePacket_NewCapture; uint64_t timestamp = FileIO::GetModifiedTimestamp(captures.back().c_str()); ser.Serialise("", idx); ser.Serialise("", timestamp); ser.Serialise("", captures.back()); uint32_t len = 128*1024; byte *thumb = new byte[len]; RENDERDOC_GetThumbnail(captures.back().c_str(), thumb, len); size_t l = len; ser.Serialise("", len); ser.SerialiseBuffer("", thumb, l); delete[] thumb; } } if(curtime < pingtime && packetType == ePacket_Noop) { if(client->IsRecvDataWaiting()) { PacketType type; Serialiser *recvser = NULL; if(!RecvPacket(client, type, &recvser)) SAFE_DELETE(client); if(client == NULL) { SAFE_DELETE(recvser); continue; } else if(type == ePacket_TriggerCapture) { RenderDoc::Inst().TriggerCapture(); } else if(type == ePacket_QueueCapture) { uint32_t frameNum = 0; recvser->Serialise("", frameNum); RenderDoc::Inst().QueueCapture(frameNum); } else if(type == ePacket_CopyCapture) { vector<wstring> caps = RenderDoc::Inst().GetCaptures(); uint32_t id = 0; recvser->Serialise("", id); if(id < caps.size()) { ser.Serialise("", id); if(!SendPacket(client, ePacket_CopyCapture, ser)) { SAFE_DELETE(client); continue; } ser.Rewind(); if(!SendChunkedFile(client, ePacket_CopyCapture, caps[id].c_str(), ser, NULL)) { SAFE_DELETE(client); continue; } RenderDoc::Inst().MarkCaptureRetrieved(id); } } SAFE_DELETE(recvser); } continue; } curtime = 0; if(!SendPacket(client, packetType, ser)) { SAFE_DELETE(client); continue; } } // give up our connection { SCOPED_LOCK(RenderDoc::Inst().m_SingleClientLock); RenderDoc::Inst().m_SingleClientName = L""; } }
void Compiler::scanStmt() { char buf[256]; // temporary buffer int t; switch(tok->getnext()){ case T_IDENT: // it's an expression case T_INT: case T_FLOAT: case T_SUB: case T_BITNOT: case T_PLING: case T_STRING: case T_BACKTICK: case T_OPREN: // deal with debugging words! if(!strcmp(tok->getstring(),"dumplocs")){ cg->emit(OP_SPECIAL,0);break; } if(!strcmp(tok->getstring(),"breakpoint")){ cg->emit(OP_SPECIAL,1);break; } tok->rewind(); // put the token back // scan the expression, might be a label if(!scanExpr(true)) // clear all statements if not a func or other oddity, // or just a dummy for recreation purposes if in immediate mode. cg->emit(cg->isCompiling()?OP_ENDESTMT:OP_ENDESTMT2); break; case T_LOAD: { if(cg->isCompiling()) error("can only run 'load' in interactive mode"); Session *s; if(tok->getnext()!=T_STRING) error("expected a string after 'load'"); try { s = new Session(ses->api); s->feedFile(tok->getstring()); } catch(Exception &e){ delete s; throw e; } delete s; } break; case T_SAVE: { if(cg->isCompiling()) error("can only run 'save' in interactive mode"); if(tok->getnext()!=T_STRING) error("expected a string after 'save'"); const char *fname = tok->getstring(); FILE *a; if(!strlen(fname)) a = stdout; else a = fopen(fname,"w"); if(!a) error("cannot open file '%s'",fname); Serialiser *ser = new Serialiser(ses); ser->write(a); if(strlen(fname)) fclose(a); delete ser; } break; case T_SAVEVAR: { if(cg->isCompiling()) error("can only run 'savevar' in interactive mode"); if(tok->getnext()!=T_IDENT) error("expected a variable name after 'savevar'"); const char *vname = tok->getstring(); int vdesc = lana->consts->findOrCreateString(vname); if(tok->getnext()!=T_STRING) error("expected a string after 'savevar'"); const char *fname = tok->getstring(); // try to get the value Value *v; int id; id = lana->globs->find(vdesc); if(id>=0) { v = lana->globs->get(id); // it's a global } else { id = ses->findSesVar(vdesc); if(id<0) error("variable not found: %s",lana->consts->getStr(vdesc)); v = ses->getSesVar(id); } FILE *a; if(!strlen(fname)) a = stdout; else a = fopen(fname,"w"); if(!a) error("cannot open file '%s'",fname); Serialiser *ser = new Serialiser(ses); ser->serialiseValue(a,v,lana->consts->getStr(vdesc)); if(strlen(fname)) fclose(a); delete ser; } break; case T_FOR: scanFor(); break; case T_ENDFOR: scanEndFor(); break; case T_THIS: tok->rewind(); // put the token back if(!scanExpr(true)) // clear all statements if not a func or other oddity, // or just a dummy for recreation purposes if in immediate mode. cg->emit(cg->isCompiling()?OP_ENDESTMT:OP_ENDESTMT2); break; case T_GOTO: if(!cg->isCompiling()) error("must be compiling a function/procedure to use '%s'",tok->getstring()); scanGoto(); break; case T_ENDFUNC: scanEndFunc(); break; case T_END: if(!(lana->opFlags & LOP_STRIPCOMMENTS)) cg->emit(OP_BLANKLINE); // yes, these are wasteful .. very slightly break; case T_IF: // first we push a special value onto the compiler stack // to mark the start of this if..elseif..elseif..endif cg->current->cpush(-9999); // we scan the expression if(scanExpr()) error("cannot use a function/procedure expression in if"); // stack and output an incomplete if - but this might be a normal if, or a quick if. cg->current->cpushhere(); // now for some cleverness. Is the next token a colon? if(tok->getnext() == T_COLON){ cg->emit(OP_QUICKIF,-100); // if so, parse the next statement recursively scanStmt(); // note that we don't need to output a quick endif, since the recreator // doesn't need it! instruction *ptr = cg->current->cpoplocandcheck(OP_QUICKIF,OP_QUICKIF); // MUST be an OP_IF, no ELSE. if(!ptr) error("not a simple statement in quick-if"); // write the IF, ELSE or ELSEIF again with the correct distance *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr)); // now pop off! int n; do{ n = cg->current->cpop(); }while(n!=-9999); } else { // not - put it back! tok->rewind(); cg->emit(OP_IF,-100); } break; case T_ENDIF: { // get the corresponding OP_IF, OP_ELSEIF or OP_ELSE cg->emit(OP_ENDIF); instruction *ptr = cg->current->cpoplocandcheck(OP_IF,OP_ELSE); if(!ptr) error("mismatched endif"); // write the IF, ELSE or ELSEIF again with the correct distance *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr)); // now pop and fixup OP_JMPELSEIFs until we get the special -9999 which marked the start for(;;){ int n = cg->current->cpop(); if(n==-9999)break; // we're done! // we're not done - get the code pointer instruction *ptr = cg->current->getPtr(n*sizeof(instruction)); // make sure it's a OP_JMPELSEIF! if(INSTOP(*ptr)!=OP_JMPELSEIF) error("badly formed conditional statement"); // change it so that it jumps to the current location *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr)); } } break; case T_ELSEIF: { // pop the instruction off the stack, an OP_IF or OP_ELSEIF instruction *ptr = cg->current->cpoplocandcheck(OP_IF,OP_ELSEIF); // first we need to terminate the previous condition, so // push the location and output a OP_JMPELSEIF ready to fill in. // This will get left on the stack! cg->current->cpushhere(); cg->emit(OP_JMPELSEIF,-100); // now make the IF or ELSEIF we popped jump to this point *ptr = INST(INSTOP(*ptr),cg->current->getdiff(ptr)); // now scan the expression if(scanExpr()) error("cannot use a function/procedure expression in if"); // push the location, and.. // output an OP_ELSEIF with a dummy jump cg->current->cpushhere(); cg->emit(OP_ELSEIF,-100); } break; case T_ELSE: { // write the OP_ELSE which will become a jump forward, // but first recording the location int elseloc = cg->current->getloc(); cg->emit(OP_ELSE,-100); // now we need to make the IF jump to here // get the corresponding OP_IF or OP_ELSEIF instruction *ptr = cg->current->cpoplocandcheck(OP_IF,OP_ELSEIF); if(!ptr) error("mismatched else"); // write the IF again with the correct jump distance int diff = cg->current->getdiff(ptr); *ptr = INST(INSTOP(*ptr),diff); // now push the location of the OP_ELSE, which // will get processed by the OP_ENDIF cg->current->cpush(elseloc); } break; // pop the location case T_RETURN: if(!cg->isCompiling()) error("must be compiling a function/procedure to use '%s'",tok->getstring()); if(tok->getnext() == T_END){ // end of line? tok->rewind(); // no return value if(cg->current->ldth.flags & LDTF_RETURNS) error("functions must return a value"); cg->emit(OP_RETURN,0); } else { tok->rewind(); if(!(cg->current->ldth.flags & LDTF_RETURNS)) error("procedures cannot return a value"); if(scanExpr()) error("cannot directly return a function"); cg->emit(OP_RETURN,1); } break; case T_WHILE: if(!cg->isCompiling()) error("must be compiling a function/procedure to use '%s'",tok->getstring()); // push the current location onto the stack - this is where ENDWHILE will // jump to cg->current->cpushhere(); // we also create and push the loop data here, so that we can use break and // continue! cg->current->newloop(); // scan and output the expression if(scanExpr()) error("cannot use a function/procedure expression in `while`"); // push the WHILE onto the stack so we can write the terminating jump into it cg->current->cpushhere(); // output OP_WHILE with a dummy cg->emit(OP_WHILE,-100); break; case T_ENDWHILE: { // pop the location of the WHILE from the stack instruction *whileptr = cg->current->cpoplocandcheck(OP_WHILE,OP_WHILE); if(!whileptr) error("mismatched endwhile"); // pop the location for the backward jump instruction *jumpdest = cg->current->cpoplocation(); // output the endwhile, which will do the backward jump cg->emit(OP_ENDWHILE,cg->current->getdiff(jumpdest)); // now patch the while instruction with the forward jump to use if the // condition is false *whileptr = INST(OP_WHILE,cg->current->getdiff(whileptr)); /// and end the loop, setting the break label and popping the loop stack cg->current->endloop(); break; } case T_REPEAT: if(!cg->isCompiling()) error("must be compiling a function/procedure to use '%s'",tok->getstring()); // output OP_REPEAT, pushing its location. We don't // jump to here, though - we jump to the following opcode. // This is done just so we can check that the until matches // a repeat. See T_UNTIL. cg->current->cpushhere(); cg->current->newloop(); // push and initialise a new loop stack entry (see T_WHILE above) cg->emit(OP_REPEAT,0); break; case T_UNTIL: { // scan and output the expression if(scanExpr()) error("cannot use a function/procedure expression in `until`"); // pop the location of the OP_REPEAT from the stack instruction *ptr = cg->current->cpoplocandcheck(OP_REPEAT,OP_REPEAT); if(!ptr) error("mismatched `until'"); // increment this, because we want to save cycles by // jumping past the OP_REPEAT (which is a kind of noop) ptr++; // and output the OP_UNTIL jump cg->emit(OP_UNTIL,cg->current->getdiff(ptr)); cg->current->endloop(); // end the current loop stack entry (see T_ENDWHILE above) break; } case T_BREAK: { // we want to break out of the topmost loop on the loop stack // get address we're about to write to instruction *op = cg->current->getlocptr(); // output the break which will be patched later cg->emit(OP_BREAK,-100); // and this jump as a jump to be patched when the break label is resolved LoopData *d = cg->current->loopstack.peekptr(); if(!d) throw ParseException("break with no loop"); d->breaklabel.jumpFrom(op); } break; case T_CONTINUE: { // we want to terminate the current iteration of the topmost loop on the loop stack // and immediately start the loop code again // get address we're about to write to instruction *op = cg->current->getlocptr(); // output the break which will be patched later cg->emit(OP_CONTINUE,-100); // and this jump as a jump to be patched when the break label is resolved LoopData *d = cg->current->loopstack.peekptr(); if(!d) throw ParseException("continue with no loop"); d->continuelabel.jumpFrom(op); } break; case T_COMMENT: scanComment(true); break; default: error("unexpected token '%s'",tok->getstring()); } // see if there's a comment at the end scanPossibleComment(); if(tok->getnext()!=T_END) error("trailing garbage at end of line"); }