Add thumbnailer support and TVMVersion and TFile are now accessable from language

This commit is contained in:
2025-03-28 22:01:56 -05:00
parent 0a87476cfa
commit 0af37d2b9f
29 changed files with 1706 additions and 101 deletions

View File

@ -82,11 +82,13 @@ namespace Tesses::CrossLang
std::string name;
std::string version;
std::string info;
std::string icon="";
TVMVersion version2;
if(GetArgumentHeap(args,0,vfs) && GetArgumentHeap(args,1,strm) && GetArgument(args,2,name) && GetArgument(args,3,version) && GetArgument(args,4,info) && TVMVersion::TryParse(version,version2))
if(GetArgumentHeap(args,0,vfs) && GetArgumentHeap(args,1,strm) && GetArgument(args,2,name) && GetArgument(args,4,info) && ((GetArgument(args,3,version) && TVMVersion::TryParse(version,version2)) || GetArgument(args,3,version2)))
{
CrossArchiveCreate(vfs->vfs,strm->stream,name,version2,info);
GetArgument(args,5,icon);
CrossArchiveCreate(vfs->vfs,strm->stream,name,version2,info,icon);
}
return nullptr;
}

View File

@ -80,7 +80,7 @@ namespace Tesses::CrossLang
return json_null();
}
static TObject Json_Encode(GCList& ls2, std::vector<TObject> args)
static TObject JsonEncode(GCList& ls2, std::vector<TObject> args)
{
if(args.size() >= 1)
{
@ -141,7 +141,7 @@ namespace Tesses::CrossLang
}
return Undefined();
}
static TObject Json_Decode(GCList& ls2,std::vector<TObject> args)
static TObject JsonDecode(GCList& ls2,std::vector<TObject> args)
{
if(args.size() > 0 && std::holds_alternative<std::string>(args[0]))
{
@ -155,6 +155,31 @@ namespace Tesses::CrossLang
return Undefined();
}
#endif
std::string Json_Encode(TObject o,bool indent)
{
#if defined(CROSSLANG_ENABLE_JSON)
auto json = JsonSerialize(o);
char* txt = json_dumps(json, indent ? JSON_INDENT(4) : 0);
std::string str = txt;
free(txt);
json_decref(json);
return str;
#else
return "";
#endif
}
TObject Json_Decode(GCList ls,std::string str)
{
#if defined(CROSSLANG_ENABLE_JSON)
json_t* json = json_loadb(str.c_str(), str.size(),0,NULL);
auto res = JsonDeserialize(ls,json);
json_decref(json);
return res;
#else
return nullptr;
#endif
}
void TStd::RegisterJson(GC* gc,TRootEnvironment* env)
{
@ -162,8 +187,8 @@ namespace Tesses::CrossLang
#if defined(CROSSLANG_ENABLE_JSON)
GCList ls(gc);
TDictionary* dict = TDictionary::Create(ls);
dict->DeclareFunction(gc, "Decode","Deserialize Json",{"Json string"},Json_Decode);
dict->DeclareFunction(gc, "Encode","Serialize Json",{"any","$indent"},Json_Encode);
dict->DeclareFunction(gc, "Decode","Deserialize Json",{"Json string"},JsonDecode);
dict->DeclareFunction(gc, "Encode","Serialize Json",{"any","$indent"},JsonEncode);
gc->BarrierBegin();

View File

@ -1,4 +1,10 @@
#include "CrossLang.hpp"
#include <TessesFramework/Crypto/ClientTLSStream.hpp>
#include <TessesFramework/Http/HttpClient.hpp>
#include <TessesFramework/Mail/Smtp.hpp>
#include <TessesFramework/Streams/MemoryStream.hpp>
#include <TessesFramework/Streams/NetworkStream.hpp>
#include <TessesFramework/Streams/Stream.hpp>
#include <sys/types.h>
#include <csignal>
#include <iostream>
@ -6,8 +12,33 @@
#include <cstring>
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::Http;
using namespace Tesses::Framework::Mail;
namespace Tesses::CrossLang
{
static SMTPBody* TObjectToSMTPBody(GCList& ls,std::string mimeType, TObject obj)
{
SMTPBody* body = nullptr;
std::string text;
TByteArray* ba;
TStreamHeapObject* sho;
if(GetObject(obj,text))
{
body = new SMTPStringBody(text,mimeType);
}
else if(GetObjectHeap(obj,ba)) {
MemoryStream* ms = new MemoryStream(true);
ms->WriteBlock(ba->data.data(), ba->data.size());
ms->Seek(0L, SeekOrigin::Begin);
body = new SMTPStreamBody(mimeType,ms,true);
}
else if(GetObjectHeap(obj,sho))
{
ls.Add(sho);
body = new SMTPStreamBody(mimeType,sho->stream,false);
}
return body;
}
TServerHeapObject* TServerHeapObject::Create(GCList& ls, Tesses::Framework::Http::IHttpServer* svr)
{
TServerHeapObject* ho = new TServerHeapObject();
@ -157,7 +188,17 @@ namespace Tesses::CrossLang
dict->DeclareFunction(gc,"OpenResponseStream","Open Response Stream",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {
return TStreamHeapObject::Create(ls2, ctx->OpenResponseStream());
});
dict->DeclareFunction(gc, "ParseFormData","Parse the form data",{},[ctx](GCList& ls, std::vector<TObject> args)->TObject {
TCallable* callable;
if(GetArgumentHeap(args, 0, callable))
{
ctx->ParseFormData([callable,&ls](std::string a,std::string b, std::string c)->Tesses::Framework::Streams::Stream*{
auto res = callable->Call(ls,{a,b,c});
return new Tesses::CrossLang::TObjectStream(ls.GetGC(),res);
});
}
return nullptr;
});
dict->DeclareFunction(gc,"getNeedToParseFormData","Check if Need to parse form data",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
return ctx->NeedToParseFormData();
});
@ -169,13 +210,13 @@ namespace Tesses::CrossLang
dict->DeclareFunction(gc,"SendText","Send response text",{"text"},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
std::string text;
if(GetArgument(args2,0,text))
ctx->SendText(text);
ctx->SendText(text);
return nullptr;
});
dict->DeclareFunction(gc,"WithMimeType","Set mime type",{"mimeType"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
std::string text;
if(GetArgument(args2,0,text))
ctx->WithMimeType(text);
ctx->WithMimeType(text);
return dict;
});
dict->DeclareFunction(gc,"WithContentDisposition","Set content disposition",{"filename","inline"},[ctx,dict](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject{
@ -205,6 +246,11 @@ namespace Tesses::CrossLang
ctx->SendBytes(ba->data);
return nullptr;
});
dict->DeclareFunction(gc,"WriteHeaders","Send the headers",{},[ctx](GCList& ls, std::vector<TObject> args)->TObject{
ctx->WriteHeaders();
return nullptr;
});
// dict->DeclareFunction(gc,"getUrlWithQuery","Get original path with query parameters",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector<Tesses::CrossLang::TObject> args2)->TObject {return ctx->GetUrlWithQuery();});
dict->DeclareFunction(gc,"StartWebSocketSession","Start websocket session",{"dict"}, [ctx](GCList& ls,std::vector<TObject> args)->TObject {
TDictionary* dict;
@ -582,6 +628,143 @@ namespace Tesses::CrossLang
return nullptr;
}
static TObject Net_Smtp_Send(GCList& ls, std::vector<TObject> args)
{
TDictionary* dict;
if(GetArgumentHeap(args,0,dict))
{
//the body should be either type text/plain or text/html
//the body and attachment data can also point to bytearray of stream
//server can also be a stream
//as of right now the email is fire and forget (ie no error checking)
//check function return type just in case (this function returns a empty string if no error)
//we rolled our own smtp client
/*
dict looks like this from crosslang's point of view
{
server = {
host = "smtp.example.com",
tls = true
},
auth = {
username = "from",
password = "THEPASSWORD"
},
domain = "example.com",
from = {
name = "The name shown in the mail where it is from",
email = "from@example.com"
},
to = "to@example.com",
subject = "My little message",
body = {
type = "text/html",
data = "<h1>Hello, world</h1>"
},
attachments = [
{
name = "myimg.png",
type = "image/png",
data = embed("myimg.png")
}
]
}
*/
ls.GetGC()->BarrierBegin();
auto server = dict->GetValue("server");
TDictionary* dict2;
Tesses::Framework::Streams::Stream* strm=nullptr;
bool ownsStream=true;
TStreamHeapObject* objStrm;
if(GetObjectHeap(server,dict2))
{
auto tlsO = dict2->GetValue("tls");
auto hostO = dict2->GetValue("host");
auto portO = dict2->GetValue("port");
std::string host;
bool tls=false;
int64_t port;
GetObject(tlsO,tls);
if(!GetObject(portO, port)) port = tls ? 465 : 25;
GetObject(hostO,host);
strm = new NetworkStream(host,(uint16_t)port,false,false,false);
if(tls)
{
strm = new Framework::Crypto::ClientTLSStream(strm,true,true,host);
}
}
else if (GetObjectHeap(server, objStrm)) {
ownsStream=false;
strm = objStrm->stream;
}
Tesses::Framework::Mail::SMTPClient client(strm,ownsStream);
auto o = dict->GetValue("domain");
GetObject(o,client.domain);
o = dict->GetValue("to");
GetObject(o,client.to);
o = dict->GetValue("subject");
GetObject(o,client.subject);
o = dict->GetValue("auth");
if(GetObjectHeap(o, dict2))
{
o = dict2->GetValue("username");
GetObject(o,client.username);
o = dict2->GetValue("password");
GetObject(o, client.password);
}
o = dict->GetValue("from");
if(GetObjectHeap(o, dict2))
{
o = dict2->GetValue("email");
GetObject(o,client.from);
o = dict2->GetValue("name");
GetObject(o, client.from_name);
}
o = dict->GetValue("body");
if(GetObjectHeap(o, dict2))
{
//type, data
std::string type = "text/plain";
o = dict2->GetValue("type");
GetObject(o,type);
o = dict2->GetValue("data");
client.body = TObjectToSMTPBody(ls,type,o);
}
o = dict->GetValue("attachments");
TList* als;
if(GetObjectHeap(o,als))
{
for(int64_t i = 0; i < als->Count(); i++)
{
auto item = als->Get(i);
if(GetObjectHeap(item, dict2))
{
o = dict2->GetValue("name");
std::string name;
GetObject(o,name);
std::string type = "text/plain";
o = dict2->GetValue("type");
GetObject(o,type);
o = dict2->GetValue("data");
client.attachments.push_back(std::pair<std::string,SMTPBody*>(name,TObjectToSMTPBody(ls, type, o)));
}
}
}
ls.GetGC()->BarrierEnd();
client.Send();
return "";
}
return nullptr;
}
void TStd::RegisterNet(GC* gc, TRootEnvironment* env)
{
@ -591,6 +774,7 @@ namespace Tesses::CrossLang
TDictionary* dict = TDictionary::Create(ls);
TDictionary* http = TDictionary::Create(ls);
TDictionary* smtp = TDictionary::Create(ls);
http->DeclareFunction(gc, "HtmlEncode","Html encode",{"param"}, Net_HtmlEncode);
http->DeclareFunction(gc, "UrlEncode","Url encode query param",{"param"}, Net_UrlEncode);
@ -601,6 +785,29 @@ namespace Tesses::CrossLang
//http->DeclareFunction(gc, "ProcessServer","Process HTTP server connection",{"networkstream","server","ip","port","encrypted"},, Net_ProcessServer);
http->DeclareFunction(gc, "StreamHttpRequestBody","Create a stream request body",{"stream","mimeType"},[](GCList& ls, std::vector<TObject> args)->TObject {
std::string mimeType;
if(GetArgument(args, 1, mimeType))
{
auto res = TNative::Create(ls,new StreamHttpRequestBody(new TObjectStream(ls.GetGC(),args[0]), true, mimeType),[](void* ptr)->void {
delete static_cast<StreamHttpRequestBody*>(ptr);
});
return res;
}
return nullptr;
});
http->DeclareFunction(gc, "TextHttpRequestBody","Create a text request body",{"text","mimeType"},[](GCList& ls, std::vector<TObject> args)->TObject {
std::string text;
std::string mimeType;
if(GetArgument(args, 0, text) && GetArgument(args, 1, mimeType))
{
auto res = TNative::Create(ls,new TextHttpRequestBody(text, mimeType),[](void* ptr)->void {
delete static_cast<TextHttpRequestBody*>(ptr);
});
return res;
}
return nullptr;
});
http->DeclareFunction(gc, "MakeRequest", "Create an http request", {"url","$extra"}, Net_Http_MakeRequest);
http->DeclareFunction(gc, "ListenSimpleWithLoop", "Listen (creates application loop)", {"server","port"},Net_Http_ListenSimpleWithLoop);
//FileServer svr()
@ -626,8 +833,10 @@ namespace Tesses::CrossLang
return nullptr;
});
dict->DeclareFunction(gc, "NetworkStream","Create a network stream",{"ipv6","datagram"},Net_NetworkStream);
smtp->DeclareFunction(gc, "Send","Send email via smtp server",{"messageStruct"},Net_Smtp_Send);
gc->BarrierBegin();
dict->SetValue("Http", http);
dict->SetValue("Smtp", smtp);
env->DeclareVariable("Net", dict);
gc->BarrierEnd();
}

View File

@ -65,5 +65,8 @@ namespace Tesses::CrossLang
gc->BarrierEnd();
#endif
env->permissions.canRegisterOGC=true;
}
}

View File

@ -118,18 +118,7 @@ namespace Tesses::CrossLang
TVFSHeapObject* vfs;
return GetArgumentHeap(args,0,vfs);
}
static TObject New_Color(GCList& ls, std::vector<TObject> args)
{
int64_t r,g,b,a;
if(GetArgument(args,0,r) && GetArgument(args,1,g) && GetArgument(args,2,b))
{
if(!GetArgument(args,3,a)) {
a = 255;
}
return Tesses::Framework::Graphics::Color((uint8_t)r,(uint8_t)g,(uint8_t)b,(uint8_t)a);
}
return Tesses::Framework::Graphics::Colors::Black;
}
static TObject TypeOf(GCList& ls, std::vector<TObject> args)
{
if(args.size() < 1) return "Undefined";
@ -345,6 +334,30 @@ namespace Tesses::CrossLang
env->permissions.canRegisterRoot=true;
TDictionary* newTypes = TDictionary::Create(ls);
newTypes->DeclareFunction(gc,"Version","Create a version object",{"$major","$minor","$patch","$build","$stage"},[](GCList& ls, std::vector<TObject> args)->TObject{
int64_t major=1;
int64_t minor=0;
int64_t patch=0;
int64_t build=0;
std::string stageS="prod";
TVMVersionStage stage=TVMVersionStage::ProductionVersion;
GetArgument(args, 0,major);
GetArgument(args, 1,minor);
GetArgument(args, 2,patch);
GetArgument(args, 3,build);
GetArgument(args, 4,stageS);
if(stageS == "dev")
stage = TVMVersionStage::DevVersion;
else if(stageS == "alpha")
stage = TVMVersionStage::AlphaVersion;
else if(stageS == "beta")
stage = TVMVersionStage::BetaVersion;
return TVMVersion((uint8_t)major,(uint8_t)minor,(uint8_t)patch,(uint16_t)build,stage);
});
env->DeclareFunction(gc, "ParseLong","Parse Long from String",{"arg","$base"},ParseLong);
env->DeclareFunction(gc, "ParseDouble","Parse Double from String",{"arg"},ParseDouble);
env->DeclareFunction(gc, "YieldEmumerable","Turn yield in function into enumerable",{"closure"},YieldEnumerableFunc);
@ -428,8 +441,35 @@ namespace Tesses::CrossLang
return Undefined();
});
env->DeclareFunction(gc,"ByteArray","Create bytearray, with optional either size (to size it) or string argument (to fill byte array)",{"$data"},ByteArray);
newTypes->DeclareFunction(gc,"Color","Create a new color",{"red","green","blue","$alpha"},New_Color);
gc->BarrierBegin();
env->DeclareVariable("Version", TDictionary::Create(ls,{
TDItem("Parse",TExternalMethod::Create(ls,"Parse version from string",{"versionStr"},[](GCList& ls, std::vector<TObject> args)->TObject{
std::string str;
if(GetArgument(args, 0, str))
{
TVMVersion version;
if(TVMVersion::TryParse(str,version))
{
return version;
}
}
return nullptr;
})),
TDItem("FromByteArray",TExternalMethod::Create(ls,"Create from ByteArray",{"byteArray","$offset"},[](GCList& ls,std::vector<TObject> args)->TObject {
TByteArray* ba;
if(GetArgumentHeap(args,0, ba))
{
int64_t offset=0;
GetArgument(args, 1, offset);
if(ba->data.size() < 5) throw VMException("ByteArray too small");
size_t o = (size_t)offset;
if((o + 5) > ba->data.size() || (o + 5) < 5) throw VMException("ByteArray too small");
return TVMVersion(ba->data.data()+o);
}
return nullptr;
}))
}));
env->DeclareVariable("InvokeMethod",MethodInvoker());
env->DeclareVariable("New", newTypes);
gc->BarrierEnd();

View File

@ -78,29 +78,7 @@ namespace Tesses::CrossLang
int64_t msec;
if(GetArgument(args,0,msec))
{
if((msec % 100) == 0)
{
msec /= 100;
for(int64_t i = 0; i < msec; i++)
{
usleep(100000);
}
}
else if((msec % 10) == 0)
{
msec /= 10;
for(int64_t i = 0; i < msec; i++)
{
usleep(10000);
}
}
else
{
for(int64_t i = 0; i < msec; i++)
{
usleep(1000);
}
}
usleep(1000*msec);
}
return nullptr;
}

View File

@ -134,7 +134,9 @@ namespace Tesses::CrossLang
std::vector<std::pair<std::string,std::string>> sources;
TVMVersion version(TVM_MAJOR,TVM_MINOR,TVM_PATCH,TVM_BUILD,TVM_VERSIONSTAGE);
std::vector<std::pair<std::string,TVMVersion>> dependencies;
std::vector<std::pair<std::string,TVMVersion>> tools;
std::string info;
std::string icon;
TVFSHeapObject* vfsHO =nullptr;
ls.GetGC()->BarrierBegin();
@ -142,17 +144,23 @@ namespace Tesses::CrossLang
TObject _version = dict->GetValue("Version");
TObject _sources = dict->GetValue("Sources");
TObject _dependencies = dict->GetValue("Dependencies");
TObject _tools = dict->GetValue("Tools");
TObject _info = dict->GetValue("Info");
TObject _icon = dict->GetValue("Icon");
TObject _resourceFileSystem = dict->GetValue("ResourceFileSystem");
TObject _out = dict->GetValue("Output");
TList* _toolList;
TList* _depList; TList* srcLst;
GetObject<std::string>(_name,name);
GetObject<std::string>(_info,info);
GetObject<std::string>(_icon,icon);
GetObjectHeap(_resourceFileSystem, vfsHO);
std::string v2;
if(GetObject<std::string>(_version,v2))
TVMVersion::TryParse(v2, version);
else
GetObject(_version,version);
if(GetObjectHeap<TList*>(_dependencies,_depList))
{
@ -172,6 +180,36 @@ namespace Tesses::CrossLang
{
dependencies.push_back(std::pair<std::string,TVMVersion>(name2, version02));
}
else if(GetObject<std::string>(_name2,name2) && GetObject(_version2,version02))
{
dependencies.push_back(std::pair<std::string,TVMVersion>(name2, version02));
}
}
}
}
if(GetObjectHeap<TList*>(_tools,_toolList))
{
for(int64_t i = 0; i < _toolList->Count(); i++)
{
TObject _dep = _toolList->Get(i);
TDictionary* _depD;
if(GetObjectHeap<TDictionary*>(_dep, _depD))
{
TObject _name2 = _depD->GetValue("Name");
TObject _version2 = _depD->GetValue("Version");
std::string name2;
std::string version2;
TVMVersion version02;
if(GetObject<std::string>(_name2,name2) && GetObject<std::string>(_version2,version2) && TVMVersion::TryParse(version2,version02))
{
tools.push_back(std::pair<std::string,TVMVersion>(name2, version02));
}
else if(GetObject<std::string>(_name2,name2) && GetObject(_version2,version02))
{
tools.push_back(std::pair<std::string,TVMVersion>(name2, version02));
}
}
}
@ -220,9 +258,11 @@ namespace Tesses::CrossLang
CodeGen gen;
gen.GenRoot(n);
gen.dependencies = dependencies;
gen.tools = tools;
gen.info = info;
gen.name = name;
gen.version = version;
gen.icon = icon;
std::string outpath;
TStreamHeapObject* stream;
if(GetObjectHeap<TStreamHeapObject*>(_out, stream))
@ -274,7 +314,9 @@ namespace Tesses::CrossLang
dict->DeclareFunction(gc, "Compile", "Compile Source",{"dict"},VM_Compile);
dict->DeclareFunction(gc, "SourceToAst", "Convert source to ast", {"source"}, VM_SourceToAst);
dict->DeclareFunction(gc, "getRuntimeVersion","Get the runtime version",{},[](GCList& ls,std::vector<TObject> args)->TObject {
return TVMVersion(TVM_MAJOR,TVM_MINOR,TVM_PATCH,TVM_BUILD,TVM_VERSIONSTAGE);
});
gc->BarrierBegin();
env->DeclareVariable("VM", dict);
gc->BarrierEnd();