From 3ebdab9401aa9eee742c8fdd5683f72dec06d615 Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Thu, 23 Jan 2025 13:36:15 -0600 Subject: [PATCH] Add native plugin support, remove SDL2 --- .vscode/settings.json | 4 +- CMakeLists.txt | 24 +-- Dockerfile | 2 +- README.md | 1 - include/CrossLang.hpp | 16 +- src/compiler/lexer.cpp | 4 +- src/runtime_methods/crypto.cpp | 46 +----- src/runtime_methods/net.cpp | 78 ++++++++- src/runtime_methods/sdl2.cpp | 281 --------------------------------- src/runtime_methods/sqlite.cpp | 3 +- src/runtime_methods/std.cpp | 137 +++++++++++++++- src/vm/vm.cpp | 198 +++++++++++++++++++++-- 12 files changed, 421 insertions(+), 373 deletions(-) delete mode 100644 src/runtime_methods/sdl2.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index add527d..923535a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -72,6 +72,8 @@ "mutex": "cpp", "semaphore": "cpp", "stop_token": "cpp", - "thread": "cpp" + "thread": "cpp", + "bitset": "cpp", + "regex": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 79744f2..a5c1b57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,9 +14,9 @@ option(CROSSLANG_ENABLE_THREADING "Enable Tesses CrossLang threading" ON) option(CROSSLANG_ENABLE_SQLITE "Enable sqlite (Embedded database, supports Wii)" ON) option(CROSSLANG_ENABLE_JSON "Enable JSON" ON) option(CROSSLANG_ENABLE_PROCESS "Enable process" ON) -option(CROSSLANG_ENABLE_SDL2 "Enable SDL2 (For Drawing)" ON) option(CROSSLANG_ENABLE_TERMIOS "Enable termios (For changing terminal options)" ON) option(CROSSLANG_ENABLE_PLATFORM_FOLDERS "Enable platform folders" ON) +option(CROSSLANG_SHARED_EXECUTABLES "Link with libcrosslang_shared" ON) option(CROSSLANG_CUSTOM_CONSOLE "Enable custom Console" OFF) @@ -59,22 +59,9 @@ endif() if(CROSSLANG_ENABLE_PLATFORM_FOLDERS) target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_PLATFORM_FOLDERS) endif() +if(CROSSLANG_ENABLE_SHARED) +target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_SHARED) -if(CROSSLANG_ENABLE_SDL2) -target_compile_definitions(${CROSSLANG_TARGET_NAME} PUBLIC CROSSLANG_ENABLE_SDL2) -find_package(SDL2 REQUIRED) -find_package(SDL2_ttf REQUIRED) - - -target_include_directories(${CROSSLANG_TARGET_NAME} PUBLIC ${SDL2_INCLUDE_DIRS}) -target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC SDL2::SDL2) -if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube") -target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC SDL2_image png jpeg z) -else() -find_package(SDL2_image REQUIRED) -target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC ${SDL2_image_LIBRARIES}) -endif() -target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC ${SDL2_ttf_LIBRARIES}) endif() if("${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoWii" OR "${CMAKE_SYSTEM_NAME}" STREQUAL "NintendoGameCube") target_link_libraries(${CROSSLANG_TARGET_NAME} PUBLIC fat) @@ -89,12 +76,11 @@ target_include_directories(${CROSSLANG_TARGET_NAME} ) endfunction() -list(APPEND CROSSLANG_SOURCE +list(APPEND CROSSLANG_SOURCE src/compiler/codegen.cpp src/compiler/lexer.cpp src/compiler/parser.cpp src/compiler/ast.cpp -src/runtime_methods/sdl2.cpp src/runtime_methods/console.cpp src/runtime_methods/io.cpp src/runtime_methods/std.cpp @@ -204,7 +190,7 @@ endif() if(CROSSLANG_ENABLE_BINARIES) -if(CROSSLANG_ENABLE_SHARED) +if(CROSSLANG_ENABLE_SHARED AND CROSSLANG_SHARED_EXECUTABLES) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") add_executable(crossc src/crosslangcompiler.cpp ${CROSSLANG_WIN32_EXE_SRC}) diff --git a/Dockerfile b/Dockerfile index a608bfa..04df218 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM onedev.site.tesses.net/tesses-framework/tesses-framework:latest RUN apt update -y && \ apt install -y --no-install-recommends \ - libjansson-dev wget libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev && \ + libjansson-dev wget && \ apt clean -y && \ rm -rf /var/lib/apt/lists/* RUN mkdir /src && cd /src && git clone https://onedev.site.tesses.net/crosslang . && cd /src && mkdir build && cd build && cmake -S .. -B . && make -j4 && make install && cd / && rm -r /src diff --git a/README.md b/README.md index d0f6f7d..716ccd6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ Tesses Cross Language - [TessesFramework](https://onedev.site.tesses.net/tesses-framework) - Jansson (but can be turned off but is strongly recommended otherwise many programs will not work) - CMake -- SDL2 (but can be turned off) ## Use in docker (use my container) ```bash diff --git a/include/CrossLang.hpp b/include/CrossLang.hpp index d5fbfe9..657c0f8 100644 --- a/include/CrossLang.hpp +++ b/include/CrossLang.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #define TVM_MAJOR 1 #define TVM_MINOR 0 #define TVM_PATCH 0 @@ -650,7 +651,7 @@ class Parser { class MethodInvoker { }; -using TObject = std::variant; +using TObject = std::variant; class TRootEnvironment; class GC; class GC { @@ -842,7 +843,6 @@ class GC { bool canRegisterDictionary; bool canRegisterJSON; bool canRegisterCrypto; - bool canRegisterSDL2; bool canRegisterRoot; bool canRegisterProcess; bool canRegisterPath; @@ -889,7 +889,6 @@ class GC { static void RegisterDictionary(GC* gc, TRootEnvironment* env); static void RegisterJson(GC* gc, TRootEnvironment* env); static void RegisterCrypto(GC* gc,TRootEnvironment* env); - static void RegisterSDL2(GC* gc, TRootEnvironment* env); static void RegisterRoot(GC* gc, TRootEnvironment* env); static void RegisterPath(GC* gc, TRootEnvironment* env); static void RegisterOGC(GC* gc, TRootEnvironment* env); @@ -1436,6 +1435,13 @@ class GC { } bool GetObjectAsPath(TObject& obj, Tesses::Framework::Filesystem::VFSPath& path, bool allowString=true); bool GetArgumentAsPath(std::vector& args, size_t index, Tesses::Framework::Filesystem::VFSPath& path,bool allowString=true); - - + typedef void (*PluginFunction)(GC* gc,TRootEnvironment* env); + #if !defined(_WIN32) + #define DLLEXPORT + #else + #define DLLEXPORT __declspec(dllexport) + #endif + #define CROSSLANG_PLUGIN(plugin) DLLEXPORT extern "C" void CrossLangPluginInit(GC* gc, TRootEnvironment* env) { plugin(gc,env); } + + void LoadPlugin(GC* gc, TRootEnvironment* env, Tesses::Framework::Filesystem::VFSPath sharedObjectPath); }; \ No newline at end of file diff --git a/src/compiler/lexer.cpp b/src/compiler/lexer.cpp index 57eda66..f6a2af6 100644 --- a/src/compiler/lexer.cpp +++ b/src/compiler/lexer.cpp @@ -451,8 +451,7 @@ namespace Tesses::CrossLang } else { - Flush(); - Symbol({read}); + buffer.push_back('$'); } break; case '\"': @@ -529,7 +528,6 @@ namespace Tesses::CrossLang while(true) { int r = Read(); - std::cout << r < args) { TByteArray* byteArray; - int64_t offset; - int64_t length; - if(GetArgumentHeap(args,0,byteArray) && GetArgument(args,1,offset) && GetArgument(args,2,length)) + + if(GetArgumentHeap(args,0,byteArray)) { - std::string str={}; - size_t olen; - size_t off = (size_t)offset; - size_t len = (size_t)length; - len = std::min(std::min(byteArray->data.size(),len-off),len); - - if(len > 0) - { - - mbedtls_base64_encode((uint8_t*)str.data(), 0, &olen, byteArray->data.data()+offset,len); - str.resize(olen); - - - if(mbedtls_base64_encode((uint8_t*)str.data(), olen, &olen, byteArray->data.data()+offset,len)==0) - { - return str; - } - - - } + return Tesses::Framework::Crypto::Base64_Encode(byteArray->data); } @@ -324,23 +304,13 @@ namespace Tesses::CrossLang std::string str; if(GetArgument(args,0,str)) { - size_t olen; - TByteArray* bArray = TByteArray::Create(ls); - - - mbedtls_base64_decode(bArray->data.data(), 0, &olen, (const uint8_t*)str.data(),str.size()); - str.resize(olen); - - - if(mbedtls_base64_decode(bArray->data.data(), olen, &olen, (const uint8_t*)str.data(),str.size())==0) - { - return str; - } + bArray->data = Tesses::Framework::Crypto::Base64_Decode(str); + return bArray; } - return ""; + return nullptr; } #endif void TStd::RegisterCrypto(GC* gc,TRootEnvironment* env) @@ -364,4 +334,4 @@ namespace Tesses::CrossLang #endif } -} \ No newline at end of file +} diff --git a/src/runtime_methods/net.cpp b/src/runtime_methods/net.cpp index 79059df..92495b9 100644 --- a/src/runtime_methods/net.cpp +++ b/src/runtime_methods/net.cpp @@ -206,7 +206,59 @@ namespace Tesses::CrossLang return nullptr; }); // dict->DeclareFunction(gc,"getUrlWithQuery","Get original path with query parameters",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector args2)->TObject {return ctx->GetUrlWithQuery();}); - + dict->DeclareFunction(gc,"StartWebSocketSession","Start websocket session",{"dict"}, [ctx](GCList& ls,std::vector args)->TObject { + TDictionary* dict; + if(GetArgumentHeap(args,0,dict)) + { + + + ctx->StartWebSocketSession([dict,&ls](std::function sendMessage,std::function ping)->void{ + GCList ls2(ls.GetGC()); + dict->CallMethod(ls2,"Open",{ + TExternalMethod::Create(ls2,"Send a message",{"messageTextOrByteArray"},[sendMessage](GCList& ls,std::vector args)->TObject{ + std::string str; + TByteArray* bArray; + if(GetArgument(args,0,str)) + { + WebSocketMessage msg(str); + sendMessage(msg); + } + else if(GetArgumentHeap(args,0,bArray)) + { + WebSocketMessage msg(bArray->data); + sendMessage(msg); + } + return nullptr; + }), + TExternalMethod::Create(ls2, "Ping client", {},[ping](GCList& ls,std::vector args)->TObject { + ping(); + return nullptr; + }) + }); + }, [dict,&ls](WebSocketMessage& msg)->void { + GCList ls2(ls.GetGC()); + + TObject v; + + if(msg.isBinary) + { + auto r = TByteArray::Create(ls2); + r->data = msg.data; + v = r; + } + else + { + v = msg.ToString(); + } + + dict->CallMethod(ls2,"Receive",{v}); + }, [dict,&ls](bool close)->void { + GCList ls2(ls.GetGC()); + dict->CallMethod(ls2,"Close",{close}); + }); + } + return nullptr; + }); //dict->DeclareFunction(gc,"getOriginalPathWithQuery","Get original path with query parameters",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector args2)->TObject {return ctx->GetOriginalPathWithQuery();}); dict->DeclareFunction(gc,"getPath","Get path",{},[ctx](Tesses::CrossLang::GCList &ls2, std::vector args2)->TObject {return ctx->path;}); @@ -247,6 +299,7 @@ namespace Tesses::CrossLang TObjectHttpServer::TObjectHttpServer(GC* gc,TObject obj) { this->ls=new GCList(gc); + this->ls->Add(obj); this->obj = obj; } @@ -550,7 +603,28 @@ namespace Tesses::CrossLang //http->DeclareFunction(gc, "ProcessServer","Process HTTP server connection",{"networkstream","server","ip","port","encrypted"},, Net_ProcessServer); 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() + http->DeclareFunction(gc, "FileServer","Create a file server",{"path","allowlisting","spa"}, [](GCList& ls, std::vector args)->TObject{ + + bool allowlisting; + bool spa; + if(GetArgument(args,1,allowlisting) && GetArgument(args,2,spa)) + { + auto fserver = new FileServer(new TObjectVFS(ls.GetGC(),args[0]),true,allowlisting,spa); + return TServerHeapObject::Create(ls,fserver); + } + return nullptr; + }); + http->DeclareFunction(gc, "MountableServer","Create a server you can mount to, must mount parents before child",{"root"}, [](GCList& ls, std::vector args)->TObject{ + if(args.size() > 0) + { + auto svr = new TObjectHttpServer(ls.GetGC(), args[0]); + auto svr2 = new MountableServer(svr,true); + + return TServerHeapObject::Create(ls,svr2); + } + return nullptr; + }); dict->DeclareFunction(gc, "NetworkStream","Create a network stream",{"ipv6","datagram"},Net_NetworkStream); gc->BarrierBegin(); dict->SetValue("Http", http); diff --git a/src/runtime_methods/sdl2.cpp b/src/runtime_methods/sdl2.cpp deleted file mode 100644 index 02cf978..0000000 --- a/src/runtime_methods/sdl2.cpp +++ /dev/null @@ -1,281 +0,0 @@ -#include "CrossLang.hpp" -#if defined(CROSSLANG_ENABLE_SDL2) -#include -#endif -namespace Tesses::CrossLang -{ - #if defined(CROSSLANG_ENABLE_SDL2) - static void TObjectToRect(TDictionary* dict, SDL_Rect& rect) - { - - int64_t number; - auto obj = dict->GetValue("x"); - if(GetObject(obj,number)) rect.x = (int)number; - obj = dict->GetValue("y"); - if(GetObject(obj,number)) rect.y = (int)number; - obj = dict->GetValue("w"); - if(GetObject(obj,number)) rect.w = (int)number; - obj = dict->GetValue("h"); - if(GetObject(obj,number)) rect.h = (int)number; - - } - - static TObject SDL2_RenderDrawRect(GCList& ls, std::vector args) - { - TNative* nat; - TDictionary* dict; - if(GetArgumentHeap(args,0, nat) && GetArgumentHeap(args,1,dict)) - { - SDL_Rect rect; - TObjectToRect(dict,rect); - return (int64_t)SDL_RenderDrawRect((SDL_Renderer*)nat->GetPointer(), &rect); - } - return nullptr; - } - static TObject SDL2_RenderFillRect(GCList& ls, std::vector args) - { - TNative* nat; - TDictionary* dict; - if(GetArgumentHeap(args,0, nat) && GetArgumentHeap(args,1,dict)) - { - SDL_Rect rect; - TObjectToRect(dict,rect); - return (int64_t)SDL_RenderFillRect((SDL_Renderer*)nat->GetPointer(), &rect); - } - return nullptr; - } - static TObject SDL2_PollEvent(GCList& ls, std::vector args) - { - SDL_Event event; - if(SDL_PollEvent(&event)) - { - TDictionary* dict = TDictionary::Create(ls); - ls.GetGC()->BarrierBegin(); - dict->SetValue("Type",(int64_t)event.common.type); - dict->SetValue("Timestamp",(int64_t)event.common.timestamp); - switch(event.type) - { - case SDL_DROPBEGIN: - case SDL_DROPCOMPLETE: - - dict->SetValue("WindowId",(int64_t)event.drop.windowID); - break; - case SDL_DROPFILE: - dict->SetValue("File", Tesses::Framework::Filesystem::VFSPath(std::string(event.drop.file))); - SDL_free(event.drop.file); - break; - case SDL_DROPTEXT: - dict->SetValue("WindowId",(int64_t)event.drop.windowID); - dict->SetValue("Text", std::string(event.drop.file)); - SDL_free(event.drop.file); - break; - case SDL_MOUSEBUTTONUP: - case SDL_MOUSEBUTTONDOWN: - dict->SetValue("WindowId",(int64_t)event.button.windowID); - dict->SetValue("Which",(int64_t)event.button.which); - dict->SetValue("Button",(int64_t)event.button.button); - dict->SetValue("State",(int64_t)event.button.state); - dict->SetValue("Clicks",(int64_t)event.button.clicks); - dict->SetValue("X",(int64_t)event.button.x); - dict->SetValue("Y",(int64_t)event.button.y); - break; - case SDL_MOUSEMOTION: - dict->SetValue("WindowId",(int64_t)event.motion.windowID); - dict->SetValue("Which",(int64_t)event.motion.which); - dict->SetValue("State",(int64_t)event.motion.state); - dict->SetValue("X",(int64_t)event.motion.x); - dict->SetValue("Y",(int64_t)event.motion.y); - dict->SetValue("XRel",(int64_t)event.motion.xrel); - dict->SetValue("YRel",(int64_t)event.motion.yrel); - break; - case SDL_MOUSEWHEEL: - dict->SetValue("Direction", (int64_t)event.wheel.direction); - dict->SetValue("MouseX",(int64_t)event.wheel.mouseX); - dict->SetValue("MouseY",(int64_t)event.wheel.mouseY); - dict->SetValue("PreciseX",(double)event.wheel.preciseX); - dict->SetValue("PreciseY",(double)event.wheel.preciseY); - dict->SetValue("Which",(int64_t)event.wheel.which); - dict->SetValue("WindowId",(int64_t)event.wheel.windowID); - dict->SetValue("X",(int64_t)event.wheel.x); - dict->SetValue("Y",(int64_t)event.wheel.y); - break; - case SDL_KEYUP: - case SDL_KEYDOWN: - dict->SetValue("WindowId",(int64_t)event.key.windowID); - dict->SetValue("Repeat",(int64_t)event.key.repeat); - dict->SetValue("State",(int64_t)event.key.state); - { - TDictionary* dict2=TDictionary::Create(ls); - dict2->SetValue("Mod",(int64_t)event.key.keysym.mod); - dict2->SetValue("Scancode",(int64_t)event.key.keysym.scancode); - dict2->SetValue("Sym",(int64_t)event.key.keysym.sym); - dict->SetValue("Keysym",dict2); - } - break; - case SDL_EventType::SDL_FINGERMOTION: - dict->SetValue("Dx",event.tfinger.dx); - dict->SetValue("Dy",event.tfinger.dy); - //falls into SDL_FINGERUP/DOWN due to having same props - case SDL_EventType::SDL_FINGERUP: - case SDL_EventType::SDL_FINGERDOWN: - dict->SetValue("X",event.tfinger.x); - dict->SetValue("Y",event.tfinger.y); - - dict->SetValue("FingerId",(int64_t)event.tfinger.fingerId); - - dict->SetValue("Pressure",event.tfinger.pressure); - - dict->SetValue("TouchId",(int64_t)event.tfinger.touchId); - - dict->SetValue("WindowId",(int64_t)event.tfinger.windowID); - break; - case SDL_EventType::SDL_TEXTINPUT: - dict->SetValue("Text",event.text.text); - dict->SetValue("WindowId",(int64_t)event.text.windowID); - break; - case SDL_EventType::SDL_TEXTEDITING: - dict->SetValue("Text",event.edit.text); - dict->SetValue("Length",(int64_t)event.edit.length); - dict->SetValue("Start",(int64_t)event.edit.start); - dict->SetValue("WindowId",(int64_t)event.edit.windowID); - break; - case SDL_EventType::SDL_TEXTEDITING_EXT: - { - dict->SetValue("Text",event.editExt.text); - dict->SetValue("Length",(int64_t)event.editExt.length); - dict->SetValue("Start",(int64_t)event.editExt.start); - dict->SetValue("WindowId",(int64_t)event.editExt.windowID); - SDL_free(event.editExt.text); - } - break; - case SDL_EventType::SDL_WINDOWEVENT: - { - dict->SetValue("WindowId",(int64_t)event.window.windowID); - dict->SetValue("Event",(int64_t)event.window.event); - dict->SetValue("Data1",(int64_t)event.window.data1); - dict->SetValue("Data2",(int64_t)event.window.data2); - - } - break; - - } - ls.GetGC()->BarrierEnd(); - return dict; - } - return nullptr; - } - static TObject SDL2_RenderPresent(GCList& ls, std::vector args) - { - TNative* renderer; - if(GetArgumentHeap(args,0,renderer) && !renderer->GetDestroyed()) - { - SDL_RenderPresent((SDL_Renderer*)renderer->GetPointer()); - } - return Undefined(); - } - static TObject SDL2_RenderClear(GCList& ls, std::vector args) - { - TNative* renderer; - if(GetArgumentHeap(args,0,renderer) && !renderer->GetDestroyed()) - { - return SDL_RenderClear((SDL_Renderer*)renderer->GetPointer())==0; - } - return Undefined(); - } - static TObject SDL2_SetRenderDrawColor(GCList& ls, std::vector args) - { - TNative* renderer; - int64_t r; - int64_t g; - int64_t b; - int64_t a; - if(GetArgumentHeap(args,0,renderer) && !renderer->GetDestroyed() && GetArgument(args,1,r) && GetArgument(args,2,g) && GetArgument(args,3,b) && GetArgument(args,4,a)) - { - return SDL_SetRenderDrawColor((SDL_Renderer*)renderer->GetPointer(),(Uint8)r,(Uint8)g,(Uint8)b,(Uint8)a) == 0; - } - return Undefined(); - } - - static TObject SDL2_Init(GCList& ls, std::vector args) - { - return (int64_t)SDL_Init(SDL_INIT_EVERYTHING); - - } - - - static TObject SDL2_CreateRenderer(GCList& ls, std::vector args) - { - TNative* window; - int64_t index; - int64_t flags; - if(GetArgumentHeap(args,0,window) && !window->GetDestroyed() && GetArgument(args,1,index) && GetArgument(args,2,flags)) - { - SDL_Renderer* _renderer= SDL_CreateRenderer((SDL_Window*)window->GetPointer(),(int)index, (Uint32)flags); - if(_renderer == nullptr) return nullptr; - return TNative::Create(ls,_renderer,[](void* _ptr)-> void{ - SDL_DestroyRenderer((SDL_Renderer*)_ptr); - }); - } - return Undefined(); - } - static TObject SDL2_CreateWindow(GCList& ls, std::vector args) - { - if(args.size() == 6 && std::holds_alternative(args[0]) && std::holds_alternative(args[1]) && std::holds_alternative(args[2]) && std::holds_alternative(args[3]) && std::holds_alternative(args[4]) && std::holds_alternative(args[5])) - { - SDL_Window* window = SDL_CreateWindow(std::get(args[0]).c_str(),(int)std::get(args[1]),(int)std::get(args[2]),(int)std::get(args[3]),(int)std::get(args[4]),(uint32_t)std::get(args[5])); - if(window == nullptr) return nullptr; - - return TNative::Create(ls,window,[](void* _ptr)->void { - SDL_Window* win = (SDL_Window*)_ptr; - if(win != nullptr) SDL_DestroyWindow(win); - }); - } - return Undefined(); - } - #endif - void TStd::RegisterSDL2(GC* gc, TRootEnvironment* env) - { - - env->permissions.canRegisterSDL2=true; - #if defined(CROSSLANG_ENABLE_SDL2) - GCList ls(gc); - TDictionary* dict = TDictionary::Create(ls); - dict->DeclareFunction(gc, "RenderDrawRect","Draw a rectangle using SDL",{"renderer","dictionary_with_x_y_w_h"}, SDL2_RenderDrawRect); - dict->DeclareFunction(gc, "RenderFillRect","Fill a rectangle using SDL",{"renderer","dictionary_with_x_y_w_h"}, SDL2_RenderFillRect); - dict->DeclareFunction(gc, "RenderPresent","Present frame (you are finished with the frame)",{"renderer"},SDL2_RenderPresent); - dict->DeclareFunction(gc, "RenderClear","Clear renderer with renderer draw color",{"renderer"},SDL2_RenderClear); - dict->DeclareFunction(gc, "SetRenderDrawColor","Set SDL2 Renderer Draw Color",{"renderer","r","g","b","a"},SDL2_SetRenderDrawColor); - dict->DeclareFunction(gc, "CreateWindow","Create a SDL2 Window",{"title","x","y","w","h","flags"},SDL2_CreateWindow); - dict->DeclareFunction(gc, "CreateRenderer","Create a SDL2 Renderer",{"window",""},SDL2_CreateRenderer); - dict->DeclareFunction(gc, "PollEvent", "Get events",{},SDL2_PollEvent); - dict->DeclareFunction(gc, "Init", "Init SDL2",{},SDL2_Init); - - - gc->BarrierBegin(); - dict->SetValue("DROPBEGIN",(int64_t)SDL_DROPBEGIN); - dict->SetValue("DROPCOMPLETE",(int64_t)SDL_DROPCOMPLETE); - dict->SetValue("DROPFILE",(int64_t)SDL_DROPFILE); - dict->SetValue("DROPTEXT",(int64_t)SDL_DROPTEXT); - dict->SetValue("MOUSEBUTTONUP",(int64_t)SDL_MOUSEBUTTONUP); - dict->SetValue("MOUSEBUTTONDOWN",(int64_t)SDL_MOUSEBUTTONDOWN); - dict->SetValue("MOUSEMOTION",(int64_t)SDL_MOUSEMOTION); - dict->SetValue("KEYUP",(int64_t)SDL_KEYUP); - dict->SetValue("KEYDOWN",(int64_t)SDL_KEYDOWN); - dict->SetValue("FINGERMOTION",(int64_t)SDL_FINGERMOTION); - dict->SetValue("FINGERUP",(int64_t)SDL_FINGERUP); - dict->SetValue("FINGERDOWN",(int64_t)SDL_FINGERDOWN); - dict->SetValue("TEXTINPUT",(int64_t)SDL_TEXTINPUT); - dict->SetValue("TEXTEDITING",(int64_t)SDL_TEXTEDITING); - dict->SetValue("TEXTEDITING_EXT",(int64_t)SDL_TEXTEDITING_EXT); - dict->SetValue("WINDOWEVENT",(int64_t)SDL_WINDOWEVENT); - dict->SetValue("QUIT",(int64_t)SDL_QUIT); - - dict->SetValue("WINDOW_RESIZABLE",(int64_t)SDL_WINDOW_RESIZABLE); - dict->SetValue("WINDOW_BORDERLESS",(int64_t)SDL_WINDOW_BORDERLESS); - dict->SetValue("WINDOW_FULLSCREEN",(int64_t)SDL_WINDOW_FULLSCREEN); - dict->SetValue("WINDOW_MAXIMIZED",(int64_t)SDL_WINDOW_MAXIMIZED); - env->DeclareVariable("SDL2", dict); - gc->BarrierEnd(); - #endif - } -} diff --git a/src/runtime_methods/sqlite.cpp b/src/runtime_methods/sqlite.cpp index 414f10e..b4b2ef0 100644 --- a/src/runtime_methods/sqlite.cpp +++ b/src/runtime_methods/sqlite.cpp @@ -39,7 +39,6 @@ namespace Tesses::CrossLang { std::string name = p.ToString(); sqlite3* sqlite; - std::cout << name << std::endl; int rc =sqlite3_open(name.c_str(),&sqlite); if(rc) { @@ -136,4 +135,4 @@ namespace Tesses::CrossLang { gc->BarrierEnd(); #endif } -} \ No newline at end of file +} diff --git a/src/runtime_methods/std.cpp b/src/runtime_methods/std.cpp index 518666d..632692b 100644 --- a/src/runtime_methods/std.cpp +++ b/src/runtime_methods/std.cpp @@ -1,7 +1,61 @@ #include "CrossLang.hpp" - +#if defined(CROSSLANG_ENABLE_SHARED) +#if defined(_WIN32) +#include +#else +#include +#endif +#endif namespace Tesses::CrossLang { + #if defined(CROSSLANG_ENABLE_SHARED) + class DL { + void* handle; + public: + DL(Tesses::Framework::Filesystem::VFSPath p) + { + Tesses::Framework::Filesystem::LocalFilesystem lfs; + std::string str = lfs.VFSPathToSystem(p); + #if defined(_WIN32) + handle = LoadLibraryExA(str.c_str() , NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); + #else + handle = dlopen(str.c_str(), RTLD_LAZY); + #endif + } + template + T Resolve(std::string name) + { + #if defined(_WIN32) + return (T)GetProcAddress(handle,name.c_str()); + #else + return (T)dlsym(handle,name.c_str()); + + #endif + } + ~DL() + { + #if defined(_WIN32) + FreeLibrary(handle); + #else + dlclose(handle); + #endif + } + }; + #endif + + void LoadPlugin(GC* gc, TRootEnvironment* env, Tesses::Framework::Filesystem::VFSPath sharedObjectPath) + { + #if defined(CROSSLANG_ENABLE_SHARED) + auto ptr = std::make_shared
(sharedObjectPath); + auto cb = ptr->Resolve("CrossLangPluginInit"); + if(cb == nullptr) return; + gc->RegisterEverythingCallback([ptr,cb](GC* gc, TRootEnvironment* env)-> void{ + cb(gc,env); + }); + cb(gc,env); + #endif + } + static TObject TypeIsDefined(GCList& ls,std::vector args) { if(args.empty()) return nullptr; @@ -67,6 +121,7 @@ namespace Tesses::CrossLang static TObject TypeOf(GCList& ls, std::vector args) { if(args.size() < 1) return "Undefined"; + if(std::holds_alternative(args[0])) return "Regex"; if(std::holds_alternative(args[0])) return "Undefined"; if(std::holds_alternative(args[0])) return "Null"; if(std::holds_alternative(args[0])) return "Boolean"; @@ -91,6 +146,7 @@ namespace Tesses::CrossLang auto native = dynamic_cast(obj); auto vfs = dynamic_cast(obj); auto strm = dynamic_cast(obj); + auto svr = dynamic_cast(obj); if(dynDict != nullptr) return "DynamicDictionary"; if(dynList != nullptr) return "DynamicList"; if(strm != nullptr) @@ -104,6 +160,20 @@ namespace Tesses::CrossLang return "Stream"; } + if(svr != nullptr) + { + auto fileServer = dynamic_cast(svr->server); + auto mountableServer = dynamic_cast(svr->server); + if(fileServer != nullptr) + { + return "FileServer"; + } + if(mountableServer != nullptr) + { + return "MountableServer"; + } + return "HttpServer"; + } if(vfs != nullptr) { auto localVFS = dynamic_cast(vfs->vfs); @@ -126,6 +196,7 @@ namespace Tesses::CrossLang if(externalMethod != nullptr) return "ExternalMethod"; if(byteArray != nullptr) return "ByteArray"; if(native != nullptr) return "Native"; + return "HeapObject"; } @@ -222,7 +293,6 @@ namespace Tesses::CrossLang this->canRegisterPath=false; this->canRegisterProcess=false; this->canRegisterRoot=false; - this->canRegisterSDL2=false; this->canRegisterSqlite=false; this->canRegisterVM = false; this->locked=false; @@ -250,7 +320,59 @@ namespace Tesses::CrossLang env->DeclareFunction(gc, "TypeIsVFS","Get whether object is a virtual filesystem",{"object"},TypeIsVFS); - + env->DeclareFunction(gc, "Regex", "Create regex object",{"regex"},[](GCList& ls,std::vector args)->TObject { + std::string str; + if(GetArgument(args,0,str)) + { + std::regex regex(str); + return regex; + } + return nullptr; + }); + env->DeclareFunction(gc, "Mutex", "Create mutex",{}, [](GCList& ls,std::vector args)->TObject { + ls.GetGC()->BarrierBegin(); + auto mtx = TDictionary::Create(ls); + auto native = TNative::Create(ls, new Tesses::Framework::Threading::Mutex(),[](void* ptr)->void{ + delete static_cast(ptr); + }); + auto lock = TExternalMethod::Create(ls,"Lock the mutex",{},[native](GCList& ls, std::vector args)->TObject { + if(native->GetDestroyed()) return nullptr; + auto r = static_cast(native->GetPointer()); + r->Lock(); + return nullptr; + }); + lock->watch.push_back(native); + mtx->SetValue("Lock",lock); + + auto unlock = TExternalMethod::Create(ls,"Unlock the mutex",{},[native](GCList& ls, std::vector args)->TObject { + if(native->GetDestroyed()) return nullptr; + auto r = static_cast(native->GetPointer()); + r->Unlock(); + return nullptr; + }); + unlock->watch.push_back(native); + mtx->SetValue("Unlock",unlock); + + + auto trylock = TExternalMethod::Create(ls,"Try to lock the mutex, returns true if we aquire the lock, false if we can't due to another thread owning it",{},[native](GCList& ls, std::vector args)->TObject { + if(native->GetDestroyed()) return true; + auto r = static_cast(native->GetPointer()); + return r->TryLock(); + }); + trylock->watch.push_back(native); + mtx->SetValue("TryLock",trylock); + ls.GetGC()->BarrierEnd(); + + + auto close = TExternalMethod::Create(ls,"Try to lock the mutex, returns true if we aquire the lock, false if we can't due to another thread owning it",{},[native](GCList& ls, std::vector args)->TObject { + native->Destroy(); + return nullptr; + }); + close->watch.push_back(native); + mtx->SetValue("Close",close); + ls.GetGC()->BarrierEnd(); + return mtx; + }); env->DeclareFunction(gc, "Thread","Create thread",{"callback"},[](GCList& ls, std::vector args)-> TObject { if(args.size() == 1 && std::holds_alternative(args[0])) @@ -282,7 +404,6 @@ namespace Tesses::CrossLang RegisterJson(gc, env); RegisterDictionary(gc, env); RegisterCrypto(gc,env); - RegisterSDL2(gc, env); RegisterOGC(gc, env); RegisterProcess(gc,env); @@ -291,6 +412,14 @@ namespace Tesses::CrossLang GCList ls(gc); TDictionary* dict = TDictionary::Create(ls); + dict->DeclareFunction(gc,"LoadNativePlugin","Load a native plugin, requires a dynamic linker and shared build of libcrosslang",{"path"},[gc,env](GCList& ls, std::vector args)->TObject { + Tesses::Framework::Filesystem::VFSPath path; + if(GetArgumentAsPath(args,0,path)) + { + LoadPlugin(gc,env,path); + } + return nullptr; + }); gc->BarrierBegin(); env->SetVariable("Reflection",dict); gc->BarrierEnd(); diff --git a/src/vm/vm.cpp b/src/vm/vm.cpp index b995c3e..902468a 100644 --- a/src/vm/vm.cpp +++ b/src/vm/vm.cpp @@ -1372,7 +1372,42 @@ namespace Tesses::CrossLang { if(!cse.empty()) { GCList ls(gc); - if(std::holds_alternative(instance)) + std::regex regex; + if(GetObject(instance,regex)) + { + if(key == "Search") + { + std::string str; + if(GetArgument(args,0,str)) + { + std::smatch m; + if(std::regex_search(str,m,regex)) + { + auto myLs = TList::Create(ls); + gc->BarrierBegin(); + for(auto item : m) + { + auto itm = TDictionary::Create(ls); + itm->SetValue("Offset", (int64_t)(item.first-str.begin())); + itm->SetValue("Length",(int64_t)item.length()); + itm->SetValue("Matched",item.matched); + itm->SetValue("Text",item.str()); + myLs->Add(itm); + } + + + cse.back()->Push(gc, myLs); + gc->BarrierEnd(); + return false; + } + + } + } + + cse.back()->Push(gc, nullptr); + return false; + } + else if(std::holds_alternative(instance)) { bool flag = std::get(instance); if(key == "ToString") @@ -1709,8 +1744,23 @@ namespace Tesses::CrossLang { std::string str = std::get(instance); if(key == "IndexOf") { - char c; - if(GetArgument(args,0,c)) + std::string str2; + char c; + if(GetArgument(args,0,str2)) + { + + int64_t index = str.size(); + GetArgument(args,1,index); + + auto res = str.find(str2,(std::size_t)index); + if(res == std::string::npos) + cse.back()->Push(gc, (int64_t)-1); + else + cse.back()->Push(gc, (int64_t)res); + return false; + } + + else if(GetArgument(args,0,c)) { int64_t index = 0; GetArgument(args,1,index); @@ -1727,8 +1777,22 @@ namespace Tesses::CrossLang { } if(key == "LastIndexOf") { + std::string str2; char c; - if(GetArgument(args,0,c)) + if(GetArgument(args,0,str2)) + { + + int64_t index = str.size(); + GetArgument(args,1,index); + + auto res = str.rfind(str2,(std::size_t)index); + if(res == std::string::npos) + cse.back()->Push(gc, (int64_t)-1); + else + cse.back()->Push(gc, (int64_t)res); + return false; + } + else if(GetArgument(args,0,c)) { int64_t index = str.size(); GetArgument(args,1,index); @@ -1914,7 +1978,71 @@ namespace Tesses::CrossLang { auto env = dynamic_cast(obj); auto rootEnv = dynamic_cast(obj); auto callable = dynamic_cast(obj); - + auto svr = dynamic_cast(obj); + + if(svr != nullptr) + { + auto mountable = dynamic_cast(svr->server); + if(mountable != nullptr) + { + if(key == "Mount") + { + Tesses::Framework::Filesystem::VFSPath p; + + if(args.size() > 1 && GetArgumentAsPath(args,0,p)) + { + mountable->Mount(p.ToString(),new TObjectHttpServer(gc,args[1]),true); + cse.back()->Push(gc,nullptr); + return false; + } + } + if(key == "Unmount") + { + Tesses::Framework::Filesystem::VFSPath p; + + if(GetArgumentAsPath(args,0,p)) + { + mountable->Unmount(p.ToString()); + cse.back()->Push(gc,nullptr); + return false; + } + } + } + if(key == "Handle") + { + + if(GetArgumentHeap(args,0,dict)) + { + gc->BarrierBegin(); + auto nat = dict->GetValue("native"); + gc->BarrierEnd(); + TNative* nat2; + if(GetObjectHeap(nat,nat2)) + { + if(!nat2->GetDestroyed()) + { + auto ctx = static_cast(nat2->GetPointer()); + if(ctx != nullptr) + { + cse.back()->Push(gc,svr->server->Handle(*ctx)); + return false; + } + + } + } + } + cse.back()->Push(gc,false); + return false; + } + if(key == "Close") + { + svr->Close(); + cse.back()->Push(gc, nullptr); + return false; + } + cse.back()->Push(gc,Undefined()); + return false; + } if(rootEnv != nullptr) { //TStd::RegisterCrypto @@ -1968,8 +2096,6 @@ namespace Tesses::CrossLang { if(myEnv->permissions.canRegisterRoot && !rootEnv->permissions.locked) TStd::RegisterRoot(gc, rootEnv); - if(myEnv->permissions.canRegisterSDL2 && !rootEnv->permissions.locked) - TStd::RegisterSDL2(gc, rootEnv); if(myEnv->permissions.canRegisterSqlite && !rootEnv->permissions.locked) TStd::RegisterSqlite(gc, rootEnv); @@ -2066,13 +2192,6 @@ namespace Tesses::CrossLang { return false; } - if(key == "RegisterSDL2") - { - if((myEnv->permissions.canRegisterEverything || myEnv->permissions.canRegisterSDL2) && !rootEnv->permissions.locked) - TStd::RegisterSDL2(gc, rootEnv); - cse.back()->Push(gc,nullptr); - return false; - } if(key == "RegisterSqlite") { if((myEnv->permissions.canRegisterEverything || myEnv->permissions.canRegisterSqlite) && !rootEnv->permissions.locked) @@ -4457,10 +4576,57 @@ namespace Tesses::CrossLang { } else { - for(size_t i = 0; i < std::min(args.size(),closure->closure->args.size()); i++) + auto requiredArguments = [closure]()->size_t { - cse->env->DeclareVariable(closure->closure->args[i], args[i]); + for(size_t i =0;iclosure->args.size();i++) + { + if(closure->closure->args[i].find("$") == 0) + { + return i; + } + } + return closure->closure->args.size(); + }; + auto optionalArguments = [closure](size_t argLen)->size_t + { + for(size_t i =0;iclosure->args.size();i++) + { + if(closure->closure->args[i].find("$$") == 0) + { + return std::min(argLen,i); + } + } + return std::min(argLen,closure->closure->args.size()); + }; + auto trimStart = [](std::string txt)->std::string { + if(txt.empty()) return {}; + if(txt[0] != '$') return txt; + auto idx = txt.find_first_not_of('$'); + if(idx == std::string::npos) return {}; + return txt.substr(idx); + }; + size_t required = requiredArguments(); + + if(args.size() < requiredArguments()) + { + throw VMException("Called a function that expected at least " + std::to_string(required) + " args but got " + std::to_string(args.size())); } + + size_t i; + for( i = 0; i < optionalArguments(args.size()); i++) + { + cse->env->DeclareVariable(trimStart(closure->closure->args[i]), args[i]); + } + if(i == closure->closure->args.size()-1) + { + auto lsArgs = TList::Create(ls); + for(;iAdd(args[i]); + cse->env->DeclareVariable(trimStart(closure->closure->args[i]), lsArgs); + i = args.size(); + } + if(i